[llvm-branch-commits] [libcxx] ab863b3 - [libc++][mdspan] Implement std::mdspan class

Tobias Hieta via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Wed Jul 26 10:30:51 PDT 2023


Author: Christian Trott
Date: 2023-07-26T19:30:43+02:00
New Revision: ab863b3e73372e983ff5fb658b4bcb6577c87471

URL: https://github.com/llvm/llvm-project/commit/ab863b3e73372e983ff5fb658b4bcb6577c87471
DIFF: https://github.com/llvm/llvm-project/commit/ab863b3e73372e983ff5fb658b4bcb6577c87471.diff

LOG: [libc++][mdspan] Implement std::mdspan class

This implements P0009 std::mdspan ((https://wg21.link/p0009)),
a multidimensional span with customization points for
layouts and data access.

Co-authored-by: Damien L-G <dalg24 at gmail.com>

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

Added: 
    libcxx/include/__mdspan/mdspan.h
    libcxx/test/std/containers/views/mdspan/mdspan/CustomTestAccessors.h
    libcxx/test/std/containers/views/mdspan/mdspan/CustomTestLayouts.h
    libcxx/test/std/containers/views/mdspan/mdspan/assert.conversion.pass.cpp
    libcxx/test/std/containers/views/mdspan/mdspan/assert.index_operator.pass.cpp
    libcxx/test/std/containers/views/mdspan/mdspan/assert.size.pass.cpp
    libcxx/test/std/containers/views/mdspan/mdspan/assign.pass.cpp
    libcxx/test/std/containers/views/mdspan/mdspan/conversion.pass.cpp
    libcxx/test/std/containers/views/mdspan/mdspan/conversion.verify.cpp
    libcxx/test/std/containers/views/mdspan/mdspan/ctor.copy.pass.cpp
    libcxx/test/std/containers/views/mdspan/mdspan/ctor.default.pass.cpp
    libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_array.pass.cpp
    libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_extents.pass.cpp
    libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_integers.pass.cpp
    libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_map.pass.cpp
    libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_map_acc.pass.cpp
    libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_span.pass.cpp
    libcxx/test/std/containers/views/mdspan/mdspan/ctor.move.pass.cpp
    libcxx/test/std/containers/views/mdspan/mdspan/deduction.pass.cpp
    libcxx/test/std/containers/views/mdspan/mdspan/element_type.verify.cpp
    libcxx/test/std/containers/views/mdspan/mdspan/extents.verify.cpp
    libcxx/test/std/containers/views/mdspan/mdspan/index_operator.pass.cpp
    libcxx/test/std/containers/views/mdspan/mdspan/mapping.verify.cpp
    libcxx/test/std/containers/views/mdspan/mdspan/move.pass.cpp
    libcxx/test/std/containers/views/mdspan/mdspan/properties.pass.cpp
    libcxx/test/std/containers/views/mdspan/mdspan/swap.pass.cpp
    libcxx/test/std/containers/views/mdspan/mdspan/types.pass.cpp

Modified: 
    libcxx/docs/ReleaseNotes/17.rst
    libcxx/include/CMakeLists.txt
    libcxx/include/__mdspan/extents.h
    libcxx/include/__mdspan/layout_left.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/extents/assert.obs.pass.cpp
    libcxx/test/std/containers/views/mdspan/extents/ctor_from_array.pass.cpp
    libcxx/test/std/containers/views/mdspan/extents/ctor_from_span.pass.cpp
    libcxx/test/std/containers/views/mdspan/layout_left/assert.ctor.extents.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_right/assert.ctor.extents.pass.cpp
    libcxx/test/std/containers/views/mdspan/layout_right/assert.index_operator.pass.cpp
    libcxx/test/std/containers/views/mdspan/layout_right/assert.stride.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/ReleaseNotes/17.rst b/libcxx/docs/ReleaseNotes/17.rst
index ba17911b045a30..5aae64f1e2814f 100644
--- a/libcxx/docs/ReleaseNotes/17.rst
+++ b/libcxx/docs/ReleaseNotes/17.rst
@@ -69,6 +69,7 @@ Implemented Papers
 - P2585R0 - Improving default container formatting
 - P0408R7 - Efficient Access to ``basic_stringbuf``'s Buffer
 - P2474R2 - ``views::repeat``
+- P0009R18 - ``mdspan`` (``layout_stride`` is not done yet)
 
 Improvements and New Features
 -----------------------------

diff  --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 8d03e1b8f43c30..af873fe0ea45ba 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -484,6 +484,7 @@ set(files
   __mdspan/extents.h
   __mdspan/layout_left.h
   __mdspan/layout_right.h
+  __mdspan/mdspan.h
   __memory/addressof.h
   __memory/align.h
   __memory/aligned_alloc.h

diff  --git a/libcxx/include/__mdspan/extents.h b/libcxx/include/__mdspan/extents.h
index 42355678d60ccc..969204f6b55046 100644
--- a/libcxx/include/__mdspan/extents.h
+++ b/libcxx/include/__mdspan/extents.h
@@ -315,7 +315,8 @@ class extents {
   }
 
   template <class _OtherIndexType, size_t _Size>
-    requires(is_convertible_v<_OtherIndexType, index_type> && is_nothrow_constructible_v<index_type, _OtherIndexType> &&
+    requires(is_convertible_v<const _OtherIndexType&, index_type> &&
+             is_nothrow_constructible_v<index_type, const _OtherIndexType&> &&
              (_Size == __rank_ || _Size == __rank_dynamic_))
   explicit(_Size != __rank_dynamic_)
       _LIBCPP_HIDE_FROM_ABI constexpr extents(const array<_OtherIndexType, _Size>& __exts) noexcept
@@ -325,7 +326,8 @@ class extents {
   }
 
   template <class _OtherIndexType, size_t _Size>
-    requires(is_convertible_v<_OtherIndexType, index_type> && is_nothrow_constructible_v<index_type, _OtherIndexType> &&
+    requires(is_convertible_v<const _OtherIndexType&, index_type> &&
+             is_nothrow_constructible_v<index_type, const _OtherIndexType&> &&
              (_Size == __rank_ || _Size == __rank_dynamic_))
   explicit(_Size != __rank_dynamic_)
       _LIBCPP_HIDE_FROM_ABI constexpr extents(const span<_OtherIndexType, _Size>& __exts) noexcept

diff  --git a/libcxx/include/__mdspan/layout_left.h b/libcxx/include/__mdspan/layout_left.h
index e81e0d10b595a7..d2930904834af8 100644
--- a/libcxx/include/__mdspan/layout_left.h
+++ b/libcxx/include/__mdspan/layout_left.h
@@ -75,8 +75,9 @@ class layout_left::mapping {
   _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.");
+    _LIBCPP_ASSERT_UNCATEGORIZED(
+        __required_span_size_is_representable(__ext),
+        "layout_left::mapping extents ctor: product of extents must be representable as index_type.");
   }
 
   template <class _OtherExtents>
@@ -84,7 +85,7 @@ class layout_left::mapping {
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_OtherExtents, extents_type>)
       mapping(const mapping<_OtherExtents>& __other) noexcept
       : __extents_(__other.extents()) {
-    _LIBCPP_ASSERT(
+    _LIBCPP_ASSERT_UNCATEGORIZED(
         __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.");
   }
@@ -94,7 +95,7 @@ class layout_left::mapping {
   _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(
+    _LIBCPP_ASSERT_UNCATEGORIZED(
         __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.");
   }
@@ -122,8 +123,8 @@ class layout_left::mapping {
     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");
+    _LIBCPP_ASSERT_UNCATEGORIZED(__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;
@@ -144,7 +145,7 @@ class layout_left::mapping {
   _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");
+    _LIBCPP_ASSERT_UNCATEGORIZED(__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);
@@ -159,7 +160,7 @@ class layout_left::mapping {
   }
 
 private:
-  extents_type __extents_{}; // exposition only
+  _LIBCPP_NO_UNIQUE_ADDRESS extents_type __extents_{};
 };
 
 #endif // _LIBCPP_STD_VER >= 23

diff  --git a/libcxx/include/__mdspan/layout_right.h b/libcxx/include/__mdspan/layout_right.h
index a8a91b86c71421..6d144294789cb5 100644
--- a/libcxx/include/__mdspan/layout_right.h
+++ b/libcxx/include/__mdspan/layout_right.h
@@ -74,8 +74,9 @@ class layout_right::mapping {
   _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_right::mapping extents ctor: product of extents must be representable as index_type.");
+    _LIBCPP_ASSERT_UNCATEGORIZED(
+        __required_span_size_is_representable(__ext),
+        "layout_right::mapping extents ctor: product of extents must be representable as index_type.");
   }
 
   template <class _OtherExtents>
@@ -83,7 +84,7 @@ class layout_right::mapping {
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_OtherExtents, extents_type>)
       mapping(const mapping<_OtherExtents>& __other) noexcept
       : __extents_(__other.extents()) {
-    _LIBCPP_ASSERT(
+    _LIBCPP_ASSERT_UNCATEGORIZED(
         __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.");
   }
@@ -93,7 +94,7 @@ class layout_right::mapping {
   _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(
+    _LIBCPP_ASSERT_UNCATEGORIZED(
         __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.");
   }
@@ -121,8 +122,8 @@ class layout_right::mapping {
     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_right::mapping: out of bounds indexing");
+    _LIBCPP_ASSERT_UNCATEGORIZED(__mdspan_detail::__is_multidimensional_index_in(__extents_, __idx...),
+                                 "layout_right::mapping: out of bounds indexing");
     return [&]<size_t... _Pos>(index_sequence<_Pos...>) {
       index_type __res = 0;
       ((__res = static_cast<index_type>(__idx) + __extents_.extent(_Pos) * __res), ...);
@@ -141,7 +142,7 @@ class layout_right::mapping {
   _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_right::mapping::stride(): invalid rank index");
+    _LIBCPP_ASSERT_UNCATEGORIZED(__r < extents_type::rank(), "layout_right::mapping::stride(): invalid rank index");
     index_type __s = 1;
     for (rank_type __i = extents_type::rank() - 1; __i > __r; __i--)
       __s *= __extents_.extent(__i);
@@ -156,7 +157,7 @@ class layout_right::mapping {
   }
 
 private:
-  extents_type __extents_{}; // exposition only
+  _LIBCPP_NO_UNIQUE_ADDRESS extents_type __extents_{};
 };
 
 #endif // _LIBCPP_STD_VER >= 23

diff  --git a/libcxx/include/__mdspan/mdspan.h b/libcxx/include/__mdspan/mdspan.h
new file mode 100644
index 00000000000000..47351f113cfa8b
--- /dev/null
+++ b/libcxx/include/__mdspan/mdspan.h
@@ -0,0 +1,301 @@
+// -*- 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_MDSPAN_H
+#define _LIBCPP___MDSPAN_MDSPAN_H
+
+#include <__assert>
+#include <__config>
+#include <__fwd/mdspan.h>
+#include <__mdspan/default_accessor.h>
+#include <__mdspan/extents.h>
+#include <__type_traits/extent.h>
+#include <__type_traits/is_abstract.h>
+#include <__type_traits/is_array.h>
+#include <__type_traits/is_constructible.h>
+#include <__type_traits/is_convertible.h>
+#include <__type_traits/is_default_constructible.h>
+#include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/is_pointer.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/rank.h>
+#include <__type_traits/remove_all_extents.h>
+#include <__type_traits/remove_cv.h>
+#include <__type_traits/remove_pointer.h>
+#include <__type_traits/remove_reference.h>
+#include <__utility/integer_sequence.h>
+#include <array>
+#include <cinttypes>
+#include <cstddef>
+#include <limits>
+#include <span>
+
+#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
+
+// Helper for lightweight test checking that one did pass a layout policy as LayoutPolicy template argument
+namespace __mdspan_detail {
+template <class _Layout, class _Extents>
+concept __has_invalid_mapping = !requires { typename _Layout::template mapping<_Extents>; };
+} // namespace __mdspan_detail
+
+template <class _ElementType,
+          class _Extents,
+          class _LayoutPolicy   = layout_right,
+          class _AccessorPolicy = default_accessor<_ElementType> >
+class mdspan {
+private:
+  static_assert(__mdspan_detail::__is_extents_v<_Extents>,
+                "mdspan: Extents template parameter must be a specialization of extents.");
+  static_assert(!is_array_v<_ElementType>, "mdspan: ElementType template parameter may not be an array type");
+  static_assert(!is_abstract_v<_ElementType>, "mdspan: ElementType template parameter may not be an abstract class");
+  static_assert(is_same_v<_ElementType, typename _AccessorPolicy::element_type>,
+                "mdspan: ElementType template parameter must match AccessorPolicy::element_type");
+  static_assert(!__mdspan_detail::__has_invalid_mapping<_LayoutPolicy, _Extents>,
+                "mdspan: LayoutPolicy template parameter is invalid. A common mistake is to pass a layout mapping "
+                "instead of a layout policy");
+
+public:
+  using extents_type     = _Extents;
+  using layout_type      = _LayoutPolicy;
+  using accessor_type    = _AccessorPolicy;
+  using mapping_type     = typename layout_type::template mapping<extents_type>;
+  using element_type     = _ElementType;
+  using value_type       = remove_cv_t<element_type>;
+  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 data_handle_type = typename accessor_type::data_handle_type;
+  using reference        = typename accessor_type::reference;
+
+  _LIBCPP_HIDE_FROM_ABI static constexpr rank_type rank() noexcept { return extents_type::rank(); }
+  _LIBCPP_HIDE_FROM_ABI static constexpr rank_type rank_dynamic() noexcept { return extents_type::rank_dynamic(); }
+  _LIBCPP_HIDE_FROM_ABI static constexpr size_t static_extent(rank_type __r) noexcept {
+    return extents_type::static_extent(__r);
+  }
+  _LIBCPP_HIDE_FROM_ABI constexpr index_type extent(rank_type __r) const noexcept {
+    return __map_.extents().extent(__r);
+  };
+
+public:
+  //--------------------------------------------------------------------------------
+  // [mdspan.mdspan.cons], mdspan constructors, assignment, and destructor
+
+  _LIBCPP_HIDE_FROM_ABI constexpr mdspan()
+    requires((extents_type::rank_dynamic() > 0) && is_default_constructible_v<data_handle_type> &&
+             is_default_constructible_v<mapping_type> && is_default_constructible_v<accessor_type>)
+  = default;
+  _LIBCPP_HIDE_FROM_ABI constexpr mdspan(const mdspan&) = default;
+  _LIBCPP_HIDE_FROM_ABI constexpr mdspan(mdspan&&)      = default;
+
+  template <class... _OtherIndexTypes>
+    requires((is_convertible_v<_OtherIndexTypes, index_type> && ...) &&
+             (is_nothrow_constructible_v<index_type, _OtherIndexTypes> && ...) &&
+             ((sizeof...(_OtherIndexTypes) == rank()) || (sizeof...(_OtherIndexTypes) == rank_dynamic())) &&
+             is_constructible_v<mapping_type, extents_type> && is_default_constructible_v<accessor_type>)
+  _LIBCPP_HIDE_FROM_ABI explicit constexpr mdspan(data_handle_type __p, _OtherIndexTypes... __exts)
+      : __ptr_(std::move(__p)), __map_(extents_type(static_cast<index_type>(std::move(__exts))...)), __acc_{} {}
+
+  template <class _OtherIndexType, size_t _Size>
+    requires(is_convertible_v<const _OtherIndexType&, index_type> &&
+             is_nothrow_constructible_v<index_type, const _OtherIndexType&> &&
+             ((_Size == rank()) || (_Size == rank_dynamic())) && is_constructible_v<mapping_type, extents_type> &&
+             is_default_constructible_v<accessor_type>)
+  explicit(_Size != rank_dynamic())
+      _LIBCPP_HIDE_FROM_ABI constexpr mdspan(data_handle_type __p, const array<_OtherIndexType, _Size>& __exts)
+      : __ptr_(std::move(__p)), __map_(extents_type(__exts)), __acc_{} {}
+
+  template <class _OtherIndexType, size_t _Size>
+    requires(is_convertible_v<const _OtherIndexType&, index_type> &&
+             is_nothrow_constructible_v<index_type, const _OtherIndexType&> &&
+             ((_Size == rank()) || (_Size == rank_dynamic())) && is_constructible_v<mapping_type, extents_type> &&
+             is_default_constructible_v<accessor_type>)
+  explicit(_Size != rank_dynamic())
+      _LIBCPP_HIDE_FROM_ABI constexpr mdspan(data_handle_type __p, span<_OtherIndexType, _Size> __exts)
+      : __ptr_(std::move(__p)), __map_(extents_type(__exts)), __acc_{} {}
+
+  _LIBCPP_HIDE_FROM_ABI constexpr mdspan(data_handle_type __p, const extents_type& __exts)
+    requires(is_default_constructible_v<accessor_type> && is_constructible_v<mapping_type, const extents_type&>)
+      : __ptr_(std::move(__p)), __map_(__exts), __acc_{} {}
+
+  _LIBCPP_HIDE_FROM_ABI constexpr mdspan(data_handle_type __p, const mapping_type& __m)
+    requires(is_default_constructible_v<accessor_type>)
+      : __ptr_(std::move(__p)), __map_(__m), __acc_{} {}
+
+  _LIBCPP_HIDE_FROM_ABI constexpr mdspan(data_handle_type __p, const mapping_type& __m, const accessor_type& __a)
+      : __ptr_(std::move(__p)), __map_(__m), __acc_(__a) {}
+
+  template <class _OtherElementType, class _OtherExtents, class _OtherLayoutPolicy, class _OtherAccessor>
+    requires(is_constructible_v<mapping_type, const typename _OtherLayoutPolicy::template mapping<_OtherExtents>&> &&
+             is_constructible_v<accessor_type, const _OtherAccessor&>)
+  explicit(!is_convertible_v<const typename _OtherLayoutPolicy::template mapping<_OtherExtents>&, mapping_type> ||
+           !is_convertible_v<const _OtherAccessor&, accessor_type>)
+      _LIBCPP_HIDE_FROM_ABI constexpr mdspan(
+          const mdspan<_OtherElementType, _OtherExtents, _OtherLayoutPolicy, _OtherAccessor>& __other)
+      : __ptr_(__other.__ptr_), __map_(__other.__map_), __acc_(__other.__acc_) {
+    static_assert(is_constructible_v<data_handle_type, const typename _OtherAccessor::data_handle_type&>,
+                  "mdspan: incompatible data_handle_type for mdspan construction");
+    static_assert(
+        is_constructible_v<extents_type, _OtherExtents>, "mdspan: incompatible extents for mdspan construction");
+
+    // The following precondition is part of the standard, but is unlikely to be triggered.
+    // The extents constructor checks this and the mapping must be storing the extents, since
+    // its extents() function returns a const reference to extents_type.
+    // The only way this can be triggered is if the mapping conversion constructor would for example
+    // always construct its extents() only from the dynamic extents, instead of from the other extents.
+    if constexpr (rank() > 0) {
+      for (size_t __r = 0; __r < rank(); __r++) {
+        _LIBCPP_ASSERT_UNCATEGORIZED(
+            (static_extent(__r) == dynamic_extent) ||
+                (static_cast<index_type>(__other.extent(__r)) == static_cast<index_type>(static_extent(__r))),
+            "mdspan: conversion mismatch of source dynamic extents with static extents");
+      }
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr mdspan& operator=(const mdspan&) = default;
+  _LIBCPP_HIDE_FROM_ABI constexpr mdspan& operator=(mdspan&&)      = default;
+
+  //--------------------------------------------------------------------------------
+  // [mdspan.mdspan.members], members
+
+  template <class... _OtherIndexTypes>
+    requires((is_convertible_v<_OtherIndexTypes, index_type> && ...) &&
+             (is_nothrow_constructible_v<index_type, _OtherIndexTypes> && ...) &&
+             (sizeof...(_OtherIndexTypes) == rank()))
+  _LIBCPP_HIDE_FROM_ABI constexpr reference operator[](_OtherIndexTypes... __indices) const {
+    _LIBCPP_ASSERT_UNCATEGORIZED(__mdspan_detail::__is_multidimensional_index_in(extents(), __indices...),
+                                 "mdspan: operator[] out of bounds access");
+    return __acc_.access(__ptr_, __map_(static_cast<index_type>(std::move(__indices))...));
+  }
+
+  template <class _OtherIndexType>
+    requires(is_convertible_v<const _OtherIndexType&, index_type> &&
+             is_nothrow_constructible_v<index_type, const _OtherIndexType&>)
+  _LIBCPP_HIDE_FROM_ABI constexpr reference operator[](const array< _OtherIndexType, rank()>& __indices) const {
+    return __acc_.access(__ptr_, [&]<size_t... _Idxs>(index_sequence<_Idxs...>) {
+      return __map_(__indices[_Idxs]...);
+    }(make_index_sequence<rank()>()));
+  }
+
+  template <class _OtherIndexType>
+    requires(is_convertible_v<const _OtherIndexType&, index_type> &&
+             is_nothrow_constructible_v<index_type, const _OtherIndexType&>)
+  _LIBCPP_HIDE_FROM_ABI constexpr reference operator[](span<_OtherIndexType, rank()> __indices) const {
+    return __acc_.access(__ptr_, [&]<size_t... _Idxs>(index_sequence<_Idxs...>) {
+      return __map_(__indices[_Idxs]...);
+    }(make_index_sequence<rank()>()));
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr size_type size() const noexcept {
+    _LIBCPP_ASSERT_UNCATEGORIZED(
+        false == ([&]<size_t... _Idxs>(index_sequence<_Idxs...>) {
+          size_type __prod = 1;
+          return (__builtin_mul_overflow(__prod, extent(_Idxs), &__prod) || ... || false);
+        }(make_index_sequence<rank()>())),
+        "mdspan: size() is not representable as size_type");
+    return [&]<size_t... _Idxs>(index_sequence<_Idxs...>) {
+      return ((static_cast<size_type>(__map_.extents().extent(_Idxs))) * ... * size_type(1));
+    }(make_index_sequence<rank()>());
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool empty() const noexcept {
+    return [&]<size_t... _Idxs>(index_sequence<_Idxs...>) {
+      return (rank() > 0) && ((__map_.extents().extent(_Idxs) == index_type(0)) || ... || false);
+    }(make_index_sequence<rank()>());
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr void swap(mdspan& __x, mdspan& __y) noexcept {
+    swap(__x.__ptr_, __y.__ptr_);
+    swap(__x.__map_, __y.__map_);
+    swap(__x.__acc_, __y.__acc_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr const extents_type& extents() const noexcept { return __map_.extents(); };
+  _LIBCPP_HIDE_FROM_ABI constexpr const data_handle_type& data_handle() const noexcept { return __ptr_; };
+  _LIBCPP_HIDE_FROM_ABI constexpr const mapping_type& mapping() const noexcept { return __map_; };
+  _LIBCPP_HIDE_FROM_ABI constexpr const accessor_type& accessor() const noexcept { return __acc_; };
+
+  _LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_unique() { return mapping_type::is_always_unique(); };
+  _LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_exhaustive() { return mapping_type::is_always_exhaustive(); };
+  _LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_strided() { return mapping_type::is_always_strided(); };
+
+  _LIBCPP_HIDE_FROM_ABI constexpr bool is_unique() const { return __map_.is_unique(); };
+  _LIBCPP_HIDE_FROM_ABI constexpr bool is_exhaustive() const { return __map_.is_exhaustive(); };
+  _LIBCPP_HIDE_FROM_ABI constexpr bool is_strided() const { return __map_.is_strided(); };
+  _LIBCPP_HIDE_FROM_ABI constexpr index_type stride(rank_type __r) const { return __map_.stride(__r); };
+
+private:
+  _LIBCPP_NO_UNIQUE_ADDRESS data_handle_type __ptr_{};
+  _LIBCPP_NO_UNIQUE_ADDRESS mapping_type __map_{};
+  _LIBCPP_NO_UNIQUE_ADDRESS accessor_type __acc_{};
+
+  template <class, class, class, class>
+  friend class mdspan;
+};
+
+template <class _ElementType, class... _OtherIndexTypes>
+  requires((is_convertible_v<_OtherIndexTypes, size_t> && ...) && (sizeof...(_OtherIndexTypes) > 0))
+explicit mdspan(_ElementType*, _OtherIndexTypes...)
+    -> mdspan<_ElementType, dextents<size_t, sizeof...(_OtherIndexTypes)>>;
+
+template <class Pointer>
+  requires(is_pointer_v<remove_reference_t<Pointer>>)
+mdspan(Pointer&&) -> mdspan<remove_pointer_t<remove_reference_t<Pointer>>, extents<size_t>>;
+
+template <class CArray>
+  requires(is_array_v<CArray> && (rank_v<CArray> == 1))
+mdspan(CArray&) -> mdspan<remove_all_extents_t<CArray>, extents<size_t, extent_v<CArray, 0>>>;
+
+template <class _ElementType, class _OtherIndexType, size_t _Size>
+mdspan(_ElementType*, const array<_OtherIndexType, _Size>&) -> mdspan<_ElementType, dextents<size_t, _Size>>;
+
+template <class _ElementType, class _OtherIndexType, size_t _Size>
+mdspan(_ElementType*, span<_OtherIndexType, _Size>) -> mdspan<_ElementType, dextents<size_t, _Size>>;
+
+// This one is necessary because all the constructors take `data_handle_type`s, not
+// `_ElementType*`s, and `data_handle_type` is taken from `accessor_type::data_handle_type`, which
+// seems to throw off automatic deduction guides.
+template <class _ElementType, class _OtherIndexType, size_t... _ExtentsPack>
+mdspan(_ElementType*, const extents<_OtherIndexType, _ExtentsPack...>&)
+    -> mdspan<_ElementType, extents<_OtherIndexType, _ExtentsPack...>>;
+
+template <class _ElementType, class MappingType>
+mdspan(_ElementType*, const MappingType&)
+    -> mdspan<_ElementType, typename MappingType::extents_type, typename MappingType::layout_type>;
+
+template <class MappingType, class AccessorType>
+mdspan(const typename AccessorType::data_handle_type, const MappingType&, const AccessorType&)
+    -> mdspan<typename AccessorType::element_type,
+              typename MappingType::extents_type,
+              typename MappingType::layout_type,
+              AccessorType>;
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___MDSPAN_MDSPAN_H

diff  --git a/libcxx/include/mdspan b/libcxx/include/mdspan
index 701def50b40a4d..9082eb8bdb5518 100644
--- a/libcxx/include/mdspan
+++ b/libcxx/include/mdspan
@@ -208,6 +208,135 @@ namespace std {
   };
 }
 
+// mdspan synopsis
+
+namespace std {
+  template<class ElementType, class Extents, class LayoutPolicy = layout_right,
+           class AccessorPolicy = default_accessor<ElementType>>
+  class mdspan {
+  public:
+    using extents_type = Extents;
+    using layout_type = LayoutPolicy;
+    using accessor_type = AccessorPolicy;
+    using mapping_type = typename layout_type::template mapping<extents_type>;
+    using element_type = ElementType;
+    using value_type = remove_cv_t<element_type>;
+    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 data_handle_type = typename accessor_type::data_handle_type;
+    using reference = typename accessor_type::reference;
+
+    static constexpr rank_type rank() noexcept { return extents_type::rank(); }
+    static constexpr rank_type rank_dynamic() noexcept { return extents_type::rank_dynamic(); }
+    static constexpr size_t static_extent(rank_type r) noexcept
+      { return extents_type::static_extent(r); }
+    constexpr index_type extent(rank_type r) const noexcept { return extents().extent(r); }
+
+    // [mdspan.mdspan.cons], constructors
+    constexpr mdspan();
+    constexpr mdspan(const mdspan& rhs) = default;
+    constexpr mdspan(mdspan&& rhs) = default;
+
+    template<class... OtherIndexTypes>
+      constexpr explicit mdspan(data_handle_type ptr, OtherIndexTypes... exts);
+    template<class OtherIndexType, size_t N>
+      constexpr explicit(N != rank_dynamic())
+        mdspan(data_handle_type p, span<OtherIndexType, N> exts);
+    template<class OtherIndexType, size_t N>
+      constexpr explicit(N != rank_dynamic())
+        mdspan(data_handle_type p, const array<OtherIndexType, N>& exts);
+    constexpr mdspan(data_handle_type p, const extents_type& ext);
+    constexpr mdspan(data_handle_type p, const mapping_type& m);
+    constexpr mdspan(data_handle_type p, const mapping_type& m, const accessor_type& a);
+
+    template<class OtherElementType, class OtherExtents,
+             class OtherLayoutPolicy, class OtherAccessorPolicy>
+      constexpr explicit(see below)
+        mdspan(const mdspan<OtherElementType, OtherExtents,
+                            OtherLayoutPolicy, OtherAccessorPolicy>& other);
+
+    constexpr mdspan& operator=(const mdspan& rhs) = default;
+    constexpr mdspan& operator=(mdspan&& rhs) = default;
+
+    // [mdspan.mdspan.members], members
+    template<class... OtherIndexTypes>
+      constexpr reference operator[](OtherIndexTypes... indices) const;
+    template<class OtherIndexType>
+      constexpr reference operator[](span<OtherIndexType, rank()> indices) const;
+    template<class OtherIndexType>
+      constexpr reference operator[](const array<OtherIndexType, rank()>& indices) const;
+
+    constexpr size_type size() const noexcept;
+    [[nodiscard]] constexpr bool empty() const noexcept;
+
+    friend constexpr void swap(mdspan& x, mdspan& y) noexcept;
+
+    constexpr const extents_type& extents() const noexcept { return map_.extents(); }
+    constexpr const data_handle_type& data_handle() const noexcept { return ptr_; }
+    constexpr const mapping_type& mapping() const noexcept { return map_; }
+    constexpr const accessor_type& accessor() const noexcept { return acc_; }
+
+    static constexpr bool is_always_unique()
+      { return mapping_type::is_always_unique(); }
+    static constexpr bool is_always_exhaustive()
+      { return mapping_type::is_always_exhaustive(); }
+    static constexpr bool is_always_strided()
+      { return mapping_type::is_always_strided(); }
+
+    constexpr bool is_unique() const
+      { return map_.is_unique(); }
+    constexpr bool is_exhaustive() const
+      { return map_.is_exhaustive(); }
+    constexpr bool is_strided() const
+      { return map_.is_strided(); }
+    constexpr index_type stride(rank_type r) const
+      { return map_.stride(r); }
+
+  private:
+    accessor_type acc_;         // exposition only
+    mapping_type map_;          // exposition only
+    data_handle_type ptr_;      // exposition only
+  };
+
+  template<class CArray>
+    requires(is_array_v<CArray> && rank_v<CArray> == 1)
+    mdspan(CArray&)
+      -> mdspan<remove_all_extents_t<CArray>, extents<size_t, extent_v<CArray, 0>>>;
+
+  template<class Pointer>
+    requires(is_pointer_v<remove_reference_t<Pointer>>)
+    mdspan(Pointer&&)
+      -> mdspan<remove_pointer_t<remove_reference_t<Pointer>>, extents<size_t>>;
+
+  template<class ElementType, class... Integrals>
+    requires((is_convertible_v<Integrals, size_t> && ...) && sizeof...(Integrals) > 0)
+    explicit mdspan(ElementType*, Integrals...)
+      -> mdspan<ElementType, dextents<size_t, sizeof...(Integrals)>>;
+
+  template<class ElementType, class OtherIndexType, size_t N>
+    mdspan(ElementType*, span<OtherIndexType, N>)
+      -> mdspan<ElementType, dextents<size_t, N>>;
+
+  template<class ElementType, class OtherIndexType, size_t N>
+    mdspan(ElementType*, const array<OtherIndexType, N>&)
+      -> mdspan<ElementType, dextents<size_t, N>>;
+
+  template<class ElementType, class IndexType, size_t... ExtentsPack>
+    mdspan(ElementType*, const extents<IndexType, ExtentsPack...>&)
+      -> mdspan<ElementType, extents<IndexType, ExtentsPack...>>;
+
+  template<class ElementType, class MappingType>
+    mdspan(ElementType*, const MappingType&)
+      -> mdspan<ElementType, typename MappingType::extents_type,
+                typename MappingType::layout_type>;
+
+  template<class MappingType, class AccessorType>
+    mdspan(const typename AccessorType::data_handle_type&, const MappingType&,
+           const AccessorType&)
+      -> mdspan<typename AccessorType::element_type, typename MappingType::extents_type,
+                typename MappingType::layout_type, AccessorType>;
+}
 */
 
 #ifndef _LIBCPP_MDSPAN
@@ -219,6 +348,7 @@ namespace std {
 #include <__mdspan/extents.h>
 #include <__mdspan/layout_left.h>
 #include <__mdspan/layout_right.h>
+#include <__mdspan/mdspan.h>
 
 #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 9ff8b67a6a20fd..0b418d2b78974e 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1516,6 +1516,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_mdspan           [system] { header "__mdspan/mdspan.h" }
 module std_private_mdspan_mdspan_fwd       [system] { header "__fwd/mdspan.h" }
 
 module std_private_memory_addressof                       [system] { header "__memory/addressof.h" }

diff  --git a/libcxx/modules/std/mdspan.cppm b/libcxx/modules/std/mdspan.cppm
index 5023dfb925ea11..d92024d9a77a3d 100644
--- a/libcxx/modules/std/mdspan.cppm
+++ b/libcxx/modules/std/mdspan.cppm
@@ -27,5 +27,5 @@ export namespace std {
   using std::default_accessor;
 
   // [mdspan.mdspan], class template mdspan
-  // using std::mdspan;
+  using std::mdspan;
 } // namespace std

diff  --git a/libcxx/test/std/containers/views/mdspan/ConvertibleToIntegral.h b/libcxx/test/std/containers/views/mdspan/ConvertibleToIntegral.h
index df67c2050d4bea..d6540d8adb6a08 100644
--- a/libcxx/test/std/containers/views/mdspan/ConvertibleToIntegral.h
+++ b/libcxx/test/std/containers/views/mdspan/ConvertibleToIntegral.h
@@ -20,4 +20,25 @@ struct IntType {
   constexpr operator char() const noexcept { return val; }
 };
 
+// only non-const convertible
+struct IntTypeNC {
+  int val;
+  constexpr IntTypeNC() = default;
+  constexpr IntTypeNC(int v) noexcept : val(v){};
+
+  constexpr bool operator==(const IntType& rhs) const { return val == rhs.val; }
+  constexpr operator int() noexcept { return val; }
+  constexpr operator unsigned() { return val; }
+  constexpr operator char() noexcept { return val; }
+};
+
+// weird configurability of convertibility to int
+template<bool conv_c, bool conv_nc, bool ctor_nt_c, bool ctor_nt_nc>
+struct IntConfig {
+  int val;
+  constexpr explicit IntConfig(int val_):val(val_){}
+  constexpr operator int() noexcept(ctor_nt_nc) requires(conv_nc) { return val; }
+  constexpr operator int() const noexcept(ctor_nt_c) requires(conv_c) { return val; }
+};
+
 #endif // TEST_STD_CONTAINERS_VIEWS_MDSPAN_CONVERTIBLE_TO_INTEGRAL_H

diff  --git a/libcxx/test/std/containers/views/mdspan/extents/assert.obs.pass.cpp b/libcxx/test/std/containers/views/mdspan/extents/assert.obs.pass.cpp
index e20146f9ea5181..078d41358d8661 100644
--- a/libcxx/test/std/containers/views/mdspan/extents/assert.obs.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/extents/assert.obs.pass.cpp
@@ -6,7 +6,7 @@
 //===----------------------------------------------------------------------===//
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-// UNSUPPORTED: !libcpp-has-hardened-mode && !libcpp-has-debug-mode
+// UNSUPPORTED: !libcpp-has-debug-mode
 // XFAIL: availability-verbose_abort-missing
 
 // <mdspan>

diff  --git a/libcxx/test/std/containers/views/mdspan/extents/ctor_from_array.pass.cpp b/libcxx/test/std/containers/views/mdspan/extents/ctor_from_array.pass.cpp
index 733f2a6038ff56..ec4009e850c56b 100644
--- a/libcxx/test/std/containers/views/mdspan/extents/ctor_from_array.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/extents/ctor_from_array.pass.cpp
@@ -82,5 +82,17 @@ int main(int, char**) {
   static_assert(std::is_convertible_v<IntType, int>, "Test helper IntType unexpectedly not convertible to int");
   static_assert(!std::is_constructible_v< std::extents<unsigned long, D>, std::array<IntType, 1>>,
                 "extents constructible from illegal arguments");
+
+  // index_type is not nothrow constructible
+  static_assert(std::is_convertible_v<IntType, unsigned char>);
+  static_assert(std::is_convertible_v<const IntType&, unsigned char>);
+  static_assert(!std::is_nothrow_constructible_v<unsigned char, const IntType&>);
+  static_assert(!std::is_constructible_v<std::dextents<unsigned char, 2>, std::array<IntType, 2>>);
+
+  // convertible from non-const to index_type but not  from const
+  static_assert(std::is_convertible_v<IntTypeNC, int>);
+  static_assert(!std::is_convertible_v<const IntTypeNC&, int>);
+  static_assert(std::is_nothrow_constructible_v<int, IntTypeNC>);
+  static_assert(!std::is_constructible_v<std::dextents<int, 2>, std::array<IntTypeNC, 2>>);
   return 0;
 }

diff  --git a/libcxx/test/std/containers/views/mdspan/extents/ctor_from_span.pass.cpp b/libcxx/test/std/containers/views/mdspan/extents/ctor_from_span.pass.cpp
index b327aa4de7afec..ea9c5be48357fa 100644
--- a/libcxx/test/std/containers/views/mdspan/extents/ctor_from_span.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/extents/ctor_from_span.pass.cpp
@@ -84,5 +84,17 @@ int main(int, char**) {
   static_assert(std::is_convertible_v<IntType, int>, "Test helper IntType unexpectedly not convertible to int");
   static_assert(!std::is_constructible_v< std::extents<unsigned long, D>, std::span<IntType, 1>>,
                 "extents constructible from illegal arguments");
+
+  // index_type is not nothrow constructible
+  static_assert(std::is_convertible_v<IntType, unsigned char>);
+  static_assert(std::is_convertible_v<const IntType&, unsigned char>);
+  static_assert(!std::is_nothrow_constructible_v<unsigned char, const IntType&>);
+  static_assert(!std::is_constructible_v<std::dextents<unsigned char, 2>, std::span<IntType, 2>>);
+
+  // convertible from non-const to index_type but not  from const
+  static_assert(std::is_convertible_v<IntTypeNC, int>);
+  static_assert(!std::is_convertible_v<const IntTypeNC&, int>);
+  static_assert(std::is_nothrow_constructible_v<int, IntTypeNC>);
+  static_assert(!std::is_constructible_v<std::dextents<int, 2>, std::span<IntTypeNC, 2>>);
   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
index 4a122e09040849..34b91c38185d30 100644
--- 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
@@ -8,7 +8,7 @@
 
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-// UNSUPPORTED: !libcpp-has-hardened-mode && !libcpp-has-debug-mode
+// UNSUPPORTED: !libcpp-has-debug-mode
 // XFAIL: availability-verbose_abort-missing
 
 // <mdspan>

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
index 1d10310f42e164..47fb858b1b4e07 100644
--- 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
@@ -8,7 +8,7 @@
 
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-// UNSUPPORTED: !libcpp-has-hardened-mode && !libcpp-has-debug-mode
+// UNSUPPORTED: !libcpp-has-debug-mode
 // XFAIL: availability-verbose_abort-missing
 
 // <mdspan>

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
index 6fe792d5170fd4..822104a9f341c6 100644
--- 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
@@ -8,7 +8,7 @@
 
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-// UNSUPPORTED: !libcpp-has-hardened-mode && !libcpp-has-debug-mode
+// UNSUPPORTED: !libcpp-has-debug-mode
 // XFAIL: availability-verbose_abort-missing
 
 // <mdspan>

diff  --git a/libcxx/test/std/containers/views/mdspan/layout_right/assert.ctor.extents.pass.cpp b/libcxx/test/std/containers/views/mdspan/layout_right/assert.ctor.extents.pass.cpp
index a460a3a27b943a..0e523de847a7cc 100644
--- a/libcxx/test/std/containers/views/mdspan/layout_right/assert.ctor.extents.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/layout_right/assert.ctor.extents.pass.cpp
@@ -8,7 +8,7 @@
 
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-// UNSUPPORTED: !libcpp-has-hardened-mode && !libcpp-has-debug-mode
+// UNSUPPORTED: !libcpp-has-debug-mode
 // XFAIL: availability-verbose_abort-missing
 
 // <mdspan>

diff  --git a/libcxx/test/std/containers/views/mdspan/layout_right/assert.index_operator.pass.cpp b/libcxx/test/std/containers/views/mdspan/layout_right/assert.index_operator.pass.cpp
index 4a9c4ee780d658..f45d4b72ede4d5 100644
--- a/libcxx/test/std/containers/views/mdspan/layout_right/assert.index_operator.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/layout_right/assert.index_operator.pass.cpp
@@ -8,7 +8,7 @@
 
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-// UNSUPPORTED: !libcpp-has-hardened-mode && !libcpp-has-debug-mode
+// UNSUPPORTED: !libcpp-has-debug-mode
 // XFAIL: availability-verbose_abort-missing
 
 // <mdspan>

diff  --git a/libcxx/test/std/containers/views/mdspan/layout_right/assert.stride.pass.cpp b/libcxx/test/std/containers/views/mdspan/layout_right/assert.stride.pass.cpp
index 01b2891d93d770..7ba3f4e34c53bf 100644
--- a/libcxx/test/std/containers/views/mdspan/layout_right/assert.stride.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/layout_right/assert.stride.pass.cpp
@@ -8,7 +8,7 @@
 
 // REQUIRES: has-unix-headers
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-// UNSUPPORTED: !libcpp-has-hardened-mode && !libcpp-has-debug-mode
+// UNSUPPORTED: !libcpp-has-debug-mode
 // XFAIL: availability-verbose_abort-missing
 
 // <mdspan>

diff  --git a/libcxx/test/std/containers/views/mdspan/mdspan/CustomTestAccessors.h b/libcxx/test/std/containers/views/mdspan/mdspan/CustomTestAccessors.h
new file mode 100644
index 00000000000000..346f4977ca142e
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/CustomTestAccessors.h
@@ -0,0 +1,314 @@
+// -*- 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 TEST_STD_CONTAINERS_VIEWS_MDSPAN_MDSPAN_CUSTOM_TEST_ACCESSORS_H
+#define TEST_STD_CONTAINERS_VIEWS_MDSPAN_MDSPAN_CUSTOM_TEST_ACCESSORS_H
+
+#include <mdspan>
+#include <type_traits>
+#include <cassert>
+
+// This contains a bunch of accessors and handles which have 
diff erent properties
+// regarding constructibility and convertibility in order to test mdspan constraints
+
+// non default constructible data handle
+template <class T>
+struct no_default_ctor_handle {
+  T* ptr;
+  no_default_ctor_handle() = delete;
+  constexpr no_default_ctor_handle(T* ptr_) : ptr(ptr_) {}
+};
+
+// handle that can't convert from T to const T
+template <class T>
+struct not_const_convertible_handle {
+  T* ptr;
+  constexpr not_const_convertible_handle() : ptr(nullptr) {}
+  constexpr not_const_convertible_handle(T* ptr_) : ptr(ptr_) {}
+
+  constexpr T& operator[](size_t i) const { return ptr[i]; }
+};
+
+// handle where move has side effects
+template <class T>
+struct move_counted_handle {
+  T* ptr;
+  constexpr move_counted_handle()                           = default;
+  constexpr move_counted_handle(const move_counted_handle&) = default;
+  template <class OtherT>
+    requires(std::is_constructible_v<T*, OtherT*>)
+  constexpr move_counted_handle(const move_counted_handle<OtherT>& other) : ptr(other.ptr){};
+  constexpr move_counted_handle(move_counted_handle&& other) {
+    ptr = other.ptr;
+    if !consteval {
+      move_counter()++;
+    }
+  }
+  constexpr move_counted_handle(T* ptr_) : ptr(ptr_) {}
+
+  constexpr move_counted_handle& operator=(const move_counted_handle&) = default;
+
+  constexpr T& operator[](size_t i) const { return ptr[i]; }
+
+  static int& move_counter() {
+    static int c = 0;
+    return c;
+  }
+};
+
+// non-default constructible accessor with a bunch of 
diff erent data handles
+template <class ElementType>
+struct checked_accessor {
+  size_t N;
+  using offset_policy    = std::default_accessor<ElementType>;
+  using element_type     = ElementType;
+  using reference        = ElementType&;
+  using data_handle_type = move_counted_handle<ElementType>;
+
+  constexpr checked_accessor(size_t N_) : N(N_) {}
+  template <class OtherElementType>
+    requires(std::is_convertible_v<OtherElementType (*)[], element_type (*)[]>)
+  explicit constexpr checked_accessor(const checked_accessor<OtherElementType>& other) noexcept {
+    N = other.N;
+  }
+
+  constexpr reference access(data_handle_type p, size_t i) const noexcept {
+    assert(i < N);
+    return p[i];
+  }
+  constexpr data_handle_type offset(data_handle_type p, size_t i) const noexcept {
+    assert(i < N);
+    return data_handle_type(p.ptr + i);
+  }
+};
+
+static_assert(std::is_constructible_v<checked_accessor<const int>, const checked_accessor<int>&>);
+static_assert(!std::is_convertible_v<const checked_accessor<int>&, checked_accessor<const int>>);
+
+template <>
+struct checked_accessor<double> {
+  size_t N;
+  using offset_policy    = std::default_accessor<double>;
+  using element_type     = double;
+  using reference        = double&;
+  using data_handle_type = no_default_ctor_handle<double>;
+
+  constexpr checked_accessor(size_t N_) : N(N_) {}
+
+  template <class OtherElementType>
+    requires(std::is_convertible_v<OtherElementType (*)[], element_type (*)[]>)
+  constexpr checked_accessor(checked_accessor<OtherElementType>&& other) noexcept {
+    N = other.N;
+  }
+
+  constexpr reference access(data_handle_type p, size_t i) const noexcept {
+    assert(i < N);
+    return p.ptr[i];
+  }
+  constexpr data_handle_type offset(data_handle_type p, size_t i) const noexcept {
+    assert(i < N);
+    return p.ptr + i;
+  }
+};
+
+template <>
+struct checked_accessor<unsigned> {
+  size_t N;
+  using offset_policy    = std::default_accessor<unsigned>;
+  using element_type     = unsigned;
+  using reference        = unsigned;
+  using data_handle_type = not_const_convertible_handle<unsigned>;
+
+  constexpr checked_accessor() : N(0) {}
+  constexpr checked_accessor(size_t N_) : N(N_) {}
+  constexpr checked_accessor(const checked_accessor& acc) : N(acc.N) {}
+
+  constexpr reference access(data_handle_type p, size_t i) const noexcept {
+    assert(i < N);
+    return p[i];
+  }
+  constexpr auto offset(data_handle_type p, size_t i) const noexcept {
+    assert(i < N);
+    return p.ptr + i;
+  }
+};
+template <>
+struct checked_accessor<const unsigned> {
+  size_t N;
+  using offset_policy    = std::default_accessor<const unsigned>;
+  using element_type     = const unsigned;
+  using reference        = unsigned;
+  using data_handle_type = not_const_convertible_handle<const unsigned>;
+
+  constexpr checked_accessor() : N(0) {}
+  constexpr checked_accessor(size_t N_) : N(N_) {}
+  constexpr checked_accessor(const checked_accessor& acc) : N(acc.N) {}
+
+  template <class OtherACC>
+  constexpr explicit(std::is_const_v<OtherACC>) checked_accessor(OtherACC&& acc) : N(acc.N) {}
+
+  constexpr reference access(data_handle_type p, size_t i) const noexcept {
+    assert(i < N);
+    return p[i];
+  }
+  constexpr auto offset(data_handle_type p, size_t i) const noexcept {
+    assert(i < N);
+    return p.ptr + i;
+  }
+};
+
+template <>
+struct checked_accessor<const float> {
+  size_t N;
+  using offset_policy    = std::default_accessor<const float>;
+  using element_type     = const float;
+  using reference        = const float&;
+  using data_handle_type = move_counted_handle<const float>;
+
+  constexpr checked_accessor() : N(0) {}
+  constexpr checked_accessor(size_t N_) : N(N_) {}
+  constexpr checked_accessor(const checked_accessor& acc) : N(acc.N) {}
+
+  constexpr checked_accessor(checked_accessor<float>&& acc) : N(acc.N) {}
+
+  constexpr reference access(data_handle_type p, size_t i) const noexcept {
+    assert(i < N);
+    return p[i];
+  }
+  constexpr data_handle_type offset(data_handle_type p, size_t i) const noexcept {
+    assert(i < N);
+    return data_handle_type(p.ptr + i);
+  }
+};
+
+template <>
+struct checked_accessor<const double> {
+  size_t N;
+  using offset_policy    = std::default_accessor<const double>;
+  using element_type     = const double;
+  using reference        = const double&;
+  using data_handle_type = move_counted_handle<const double>;
+
+  constexpr checked_accessor() : N(0) {}
+  constexpr checked_accessor(size_t N_) : N(N_) {}
+  constexpr checked_accessor(const checked_accessor& acc) : N(acc.N) {}
+
+  constexpr reference access(data_handle_type p, size_t i) const noexcept {
+    assert(i < N);
+    return p[i];
+  }
+  constexpr data_handle_type offset(data_handle_type p, size_t i) const noexcept {
+    assert(i < N);
+    return data_handle_type(p.ptr + i);
+  }
+};
+
+// Data handle pair which has configurable conversion properties
+// bool template parameters are used to enable/disable ctors and assignment
+// the c is the one for const T the nc for non-const (so we can convert mdspan)
+// Note both take non-const T as template parameter though
+template <class T, bool, bool, bool, bool>
+struct conv_test_accessor_c;
+
+template <class T, bool conv_c, bool conv_nc>
+struct conv_test_accessor_nc {
+  using offset_policy    = std::default_accessor<T>;
+  using element_type     = T;
+  using reference        = T&;
+  using data_handle_type = T*;
+
+  constexpr conv_test_accessor_nc()                             = default;
+  constexpr conv_test_accessor_nc(const conv_test_accessor_nc&) = default;
+
+  template <bool b1, bool b2, bool b3, bool b4>
+  constexpr operator conv_test_accessor_c<T, b1, b2, b3, b4>()
+    requires(conv_nc)
+  {
+    return conv_test_accessor_c<T, b1, b2, b3, b4>{};
+  }
+  template <bool b1, bool b2, bool b3, bool b4>
+  constexpr operator conv_test_accessor_c<T, b1, b2, b3, b4>() const
+    requires(conv_c)
+  {
+    return conv_test_accessor_c<T, b1, b2, b3, b4>{};
+  }
+
+  constexpr reference access(data_handle_type p, size_t i) const noexcept { return p[i]; }
+  constexpr data_handle_type offset(data_handle_type p, size_t i) const noexcept { return p + i; }
+};
+
+template <class T, bool ctor_c, bool ctor_mv, bool assign_c, bool assign_mv>
+struct conv_test_accessor_c {
+  using offset_policy    = std::default_accessor<const T>;
+  using element_type     = const T;
+  using reference        = const T&;
+  using data_handle_type = const T*;
+
+  constexpr conv_test_accessor_c()                            = default;
+  constexpr conv_test_accessor_c(const conv_test_accessor_c&) = default;
+
+  template <bool b1, bool b2>
+  constexpr conv_test_accessor_c(const conv_test_accessor_nc<T, b1, b2>&)
+    requires(ctor_c)
+  {}
+  template <bool b1, bool b2>
+  constexpr conv_test_accessor_c(conv_test_accessor_nc<T, b1, b2>&&)
+    requires(ctor_mv)
+  {}
+  template <bool b1, bool b2>
+  constexpr conv_test_accessor_c& operator=(const conv_test_accessor_nc<T, b1, b2>&)
+    requires(assign_c)
+  {
+    return {};
+  }
+  template <bool b1, bool b2>
+  constexpr conv_test_accessor_c& operator=(conv_test_accessor_nc<T, b1, b2>&&)
+    requires(assign_mv)
+  {
+    return {};
+  }
+
+  constexpr reference access(data_handle_type p, size_t i) const noexcept { return p[i]; }
+  constexpr data_handle_type offset(data_handle_type p, size_t i) const noexcept { return p + i; }
+};
+
+template <class ElementType>
+struct convertible_accessor_but_not_handle {
+  size_t N;
+  using offset_policy    = std::default_accessor<ElementType>;
+  using element_type     = ElementType;
+  using reference        = ElementType&;
+  using data_handle_type = not_const_convertible_handle<element_type>;
+
+  constexpr convertible_accessor_but_not_handle() = default;
+  template <class OtherElementType>
+    requires(std::is_convertible_v<OtherElementType (*)[], element_type (*)[]>)
+  explicit constexpr convertible_accessor_but_not_handle(
+      const convertible_accessor_but_not_handle<OtherElementType>& other) noexcept {
+    N = other.N;
+  }
+
+  constexpr reference access(data_handle_type p, size_t i) const noexcept {
+    assert(i < N);
+    return p[i];
+  }
+  constexpr data_handle_type offset(data_handle_type p, size_t i) const noexcept {
+    assert(i < N);
+    return data_handle_type(p.ptr + i);
+  }
+};
+
+#endif // TEST_STD_CONTAINERS_VIEWS_MDSPAN_MDSPAN_CUSTOM_TEST_ACCESSORS_H

diff  --git a/libcxx/test/std/containers/views/mdspan/mdspan/CustomTestLayouts.h b/libcxx/test/std/containers/views/mdspan/mdspan/CustomTestLayouts.h
new file mode 100644
index 00000000000000..e1d6e80afbd84c
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/CustomTestLayouts.h
@@ -0,0 +1,325 @@
+// -*- 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 TEST_STD_CONTAINERS_VIEWS_MDSPAN_MDSPAN_CUSTOM_TEST_LAYOUTS_H
+#define TEST_STD_CONTAINERS_VIEWS_MDSPAN_MDSPAN_CUSTOM_TEST_LAYOUTS_H
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <cinttypes>
+#include <concepts>
+#include <cstddef>
+#include <limits>
+#include <mdspan>
+#include <type_traits>
+#include <utility>
+
+// Layout that wraps indices to test some idiosyncratic behavior
+// - basically it is a layout_left where indicies are first wrapped i.e. i%Wrap
+// - only accepts integers as indices
+// - is_always_strided and is_always_unique are false
+// - is_strided and is_unique are true if all extents are smaller than Wrap
+// - not default constructible
+// - not extents constructible
+// - not trivally copyable
+// - does not check dynamic to static extent conversion in converting ctor
+// - check via side-effects that mdspan::swap calls mappings swap via ADL
+
+struct not_extents_constructible_tag {};
+
+template <size_t Wrap>
+class layout_wrapping_integral {
+public:
+  template <class Extents>
+  class mapping;
+};
+
+template <size_t WrapArg>
+template <class Extents>
+class layout_wrapping_integral<WrapArg>::mapping {
+  static constexpr typename Extents::index_type Wrap = static_cast<typename Extents::index_type>(WrapArg);
+
+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_wrapping_integral<Wrap>;
+
+private:
+  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, std::min(ext.extent(r), Wrap), &prod);
+      if (overflowed)
+        return false;
+    }
+    return true;
+  }
+
+public:
+  constexpr mapping() noexcept = delete;
+  constexpr mapping(const mapping& other) noexcept : extents_(other.extents()){};
+  constexpr mapping(extents_type&& ext) noexcept
+    requires(Wrap == 8)
+      : extents_(ext) {}
+  constexpr mapping(const extents_type& ext, not_extents_constructible_tag) noexcept : extents_(ext) {}
+
+  template <class OtherExtents>
+    requires(std::is_constructible_v<extents_type, OtherExtents> && (Wrap != 8))
+  constexpr explicit(!std::is_convertible_v<OtherExtents, extents_type>)
+      mapping(const mapping<OtherExtents>& other) noexcept {
+    std::array<index_type, extents_type::rank_dynamic()> dyn_extents;
+    rank_type count = 0;
+    for (rank_type r = 0; r < extents_type::rank(); r++) {
+      if (extents_type::static_extent(r) == std::dynamic_extent) {
+        dyn_extents[count++] = other.extents().extent(r);
+      }
+    }
+    extents_ = extents_type(dyn_extents);
+  }
+  template <class OtherExtents>
+    requires(std::is_constructible_v<extents_type, OtherExtents> && (Wrap == 8))
+  constexpr explicit(!std::is_convertible_v<OtherExtents, extents_type>)
+      mapping(mapping<OtherExtents>&& other) noexcept {
+    std::array<index_type, extents_type::rank_dynamic()> dyn_extents;
+    rank_type count = 0;
+    for (rank_type r = 0; r < extents_type::rank(); r++) {
+      if (extents_type::static_extent(r) == std::dynamic_extent) {
+        dyn_extents[count++] = other.extents().extent(r);
+      }
+    }
+    extents_ = extents_type(dyn_extents);
+  }
+
+  constexpr mapping& operator=(const mapping& other) noexcept {
+    extents_ = other.extents_;
+    return *this;
+  };
+
+  constexpr const extents_type& extents() const noexcept { return extents_; }
+
+  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) < Wrap ? extents_.extent(r) : Wrap;
+    return size;
+  }
+
+  template <std::integral... Indices>
+    requires((sizeof...(Indices) == extents_type::rank()) && (std::is_convertible_v<Indices, index_type> && ...) &&
+             (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) % Wrap)...};
+    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) < Wrap ? extents_.extent(extents_type::rank() - 1 - Pos)
+                                                                      : Wrap) *
+                  res),
+       ...);
+      return res;
+    }(std::make_index_sequence<sizeof...(Indices)>());
+  }
+
+  static constexpr bool is_always_unique() noexcept { return false; }
+  static constexpr bool is_always_exhaustive() noexcept { return true; }
+  static constexpr bool is_always_strided() noexcept { return false; }
+
+  constexpr bool is_unique() const noexcept {
+    for (rank_type r = 0; r < extents_type::rank(); r++) {
+      if (extents_.extent(r) > Wrap)
+        return false;
+    }
+    return true;
+  }
+  static constexpr bool is_exhaustive() noexcept { return true; }
+  constexpr bool is_strided() const noexcept {
+    for (rank_type r = 0; r < extents_type::rank(); r++) {
+      if (extents_.extent(r) > Wrap)
+        return false;
+    }
+    return true;
+  }
+
+  constexpr index_type stride(rank_type r) const noexcept
+    requires(extents_type::rank() > 0)
+  {
+    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())
+  friend constexpr bool operator==(const mapping& lhs, const mapping<OtherExtents>& rhs) noexcept {
+    return lhs.extents() == rhs.extents();
+  }
+
+  friend constexpr void swap(mapping& x, mapping& y) noexcept {
+    swap(x.extents_, y.extents_);
+    if !consteval {
+      swap_counter()++;
+    }
+  }
+
+  static int& swap_counter() {
+    static int value = 0;
+    return value;
+  }
+
+private:
+  extents_type extents_{};
+};
+
+template <class Extents>
+constexpr auto construct_mapping(std::layout_left, Extents exts) {
+  return std::layout_left::mapping<Extents>(exts);
+}
+
+template <class Extents>
+constexpr auto construct_mapping(std::layout_right, Extents exts) {
+  return std::layout_right::mapping<Extents>(exts);
+}
+
+template <size_t Wraps, class Extents>
+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
+class always_convertible_layout {
+public:
+  template <class Extents>
+  class mapping;
+};
+
+template <class Extents>
+class always_convertible_layout::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  = always_convertible_layout;
+
+private:
+  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;
+  }
+
+public:
+  constexpr mapping() noexcept = delete;
+  constexpr mapping(const mapping& other) noexcept : extents_(other.extents()){};
+  constexpr mapping(const extents_type& ext) noexcept : extents_(ext){};
+
+  template <class OtherExtents>
+  constexpr mapping(const mapping<OtherExtents>& other) noexcept {
+    if constexpr (extents_type::rank() == OtherExtents::rank()) {
+      std::array<index_type, extents_type::rank_dynamic()> dyn_extents;
+      rank_type count = 0;
+      for (rank_type r = 0; r < extents_type::rank(); r++) {
+        if (extents_type::static_extent(r) == std::dynamic_extent) {
+          dyn_extents[count++] = other.extents().extent(r);
+        }
+      }
+      extents_ = extents_type(dyn_extents);
+    } else {
+      extents_ = extents_type();
+    }
+  }
+
+  constexpr mapping& operator=(const mapping& other) noexcept {
+    extents_ = other.extents_;
+    return *this;
+  };
+
+  constexpr const extents_type& extents() const noexcept { return extents_; }
+
+  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 <std::integral... Indices>
+    requires((sizeof...(Indices) == extents_type::rank()) && (std::is_convertible_v<Indices, index_type> && ...) &&
+             (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)>());
+  }
+
+  static constexpr bool is_always_unique() noexcept { return false; }
+  static constexpr bool is_always_exhaustive() noexcept { return true; }
+  static constexpr bool is_always_strided() noexcept { return false; }
+
+  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 r) const noexcept
+    requires(extents_type::rank() > 0)
+  {
+    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())
+  friend constexpr bool operator==(const mapping& lhs, const mapping<OtherExtents>& rhs) noexcept {
+    return lhs.extents() == rhs.extents();
+  }
+
+  friend constexpr void swap(mapping& x, mapping& y) noexcept {
+    swap(x.extents_, y.extents_);
+    if !consteval {
+      swap_counter()++;
+    }
+  }
+
+  static int& swap_counter() {
+    static int value = 0;
+    return value;
+  }
+
+private:
+  extents_type extents_{};
+};
+#endif                     // TEST_STD_CONTAINERS_VIEWS_MDSPAN_MDSPAN_CUSTOM_TEST_LAYOUTS_H

diff  --git a/libcxx/test/std/containers/views/mdspan/mdspan/assert.conversion.pass.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/assert.conversion.pass.cpp
new file mode 100644
index 00000000000000..a8ea360a9f59ae
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/assert.conversion.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
+//
+//===----------------------------------------------------------------------===//
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: !libcpp-has-debug-mode
+// XFAIL: availability-verbose_abort-missing
+
+// <mdspan>
+
+// template<class OtherElementType, class OtherExtents,
+//         class OtherLayoutPolicy, class OtherAccessor>
+//  constexpr explicit(see below)
+//    mdspan(const mdspan<OtherElementType, OtherExtents,
+//                        OtherLayoutPolicy, OtherAccessor>& other);
+//
+// Constraints:
+//   - is_constructible_v<mapping_type, const OtherLayoutPolicy::template mapping<OtherExtents>&> is true, and
+//   - is_constructible_v<accessor_type, const OtherAccessor&> is true.
+// Mandates:
+//   - is_constructible_v<data_handle_type, const OtherAccessor::data_handle_type&> is
+//   - is_constructible_v<extents_type, OtherExtents> is true.
+//
+// Preconditions:
+//   - For each rank index r of extents_type, static_extent(r) == dynamic_extent || static_extent(r) == other.extent(r) is true.
+//   - [0, map_.required_span_size()) is an accessible range of ptr_ and acc_ for values of ptr_, map_, and acc_ after the invocation of this constructor.
+//
+// Effects:
+//   - Direct-non-list-initializes ptr_ with other.ptr_,
+//   - direct-non-list-initializes map_ with other.map_, and
+//   - direct-non-list-initializes acc_ with other.acc_.
+//
+// Remarks: The expression inside explicit is equivalent to:
+//   !is_convertible_v<const OtherLayoutPolicy::template mapping<OtherExtents>&, mapping_type>
+//   || !is_convertible_v<const OtherAccessor&, accessor_type>
+
+#include <array>
+#include <cassert>
+#include <mdspan>
+
+#include "check_assertion.h"
+#include "CustomTestLayouts.h"
+
+// We use a funky mapping in this test that doesn't check the dynamic/static extents mismatch itself
+int main(int, char**) {
+  constexpr size_t D = std::dynamic_extent;
+  std::array<float, 10> data;
+  typename layout_wrapping_integral<4>::template mapping<std::dextents<int, 2>> src_map(
+      std::dextents<int, 2>(5, 2), not_extents_constructible_tag());
+  std::mdspan<float, std::dextents<int, 2>, layout_wrapping_integral<4>> arg(data.data(), src_map);
+
+  // working case
+  {
+    [[maybe_unused]] std::mdspan<float, std::extents<size_t, D, 2>, layout_wrapping_integral<4>> m(arg); // should work
+  }
+  // mismatch of static extent
+  {
+    TEST_LIBCPP_ASSERT_FAILURE(
+        ([=] { std::mdspan<float, std::extents<size_t, D, 3>, layout_wrapping_integral<4>> m(arg); }()),
+        "mdspan: conversion mismatch of source dynamic extents with static extents");
+  }
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/views/mdspan/mdspan/assert.index_operator.pass.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/assert.index_operator.pass.cpp
new file mode 100644
index 00000000000000..11d013e18e93b8
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/assert.index_operator.pass.cpp
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// 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-has-debug-mode
+// XFAIL: availability-verbose_abort-missing
+
+// <mdspan>
+
+// template<class... OtherIndexTypes>
+//   constexpr reference operator[](OtherIndexTypes... indices) const;
+// Constraints:
+//   - (is_convertible_v<OtherIndexTypes, index_type> && ...) is true,
+//   - (is_nothrow_constructible_v<index_type, OtherIndexTypes> && ...) is true, and
+//   - sizeof...(OtherIndexTypes) == rank() is true.
+//
+// Let I be extents_type::index-cast(std::move(indices)).
+//
+// Preconditions: I is a multidimensional index in extents().
+//   Note 1: This implies that map_(I) < map_.required_span_size() is true.
+//
+// Effects: Equivalent to:
+//   return acc_.access(ptr_, map_(static_cast<index_type>(std::move(indices))...));
+
+#include <mdspan>
+#include <cassert>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+  float data[1024];
+  // value out of range
+  {
+    std::mdspan m(data, std::extents<unsigned char, 5>());
+    TEST_LIBCPP_ASSERT_FAILURE(m[-1], "mdspan: operator[] out of bounds access");
+    TEST_LIBCPP_ASSERT_FAILURE(m[-130], "mdspan: operator[] out of bounds access");
+    TEST_LIBCPP_ASSERT_FAILURE(m[5], "mdspan: operator[] out of bounds access");
+    TEST_LIBCPP_ASSERT_FAILURE(m[1000], "mdspan: operator[] out of bounds access");
+  }
+  {
+    std::mdspan m(data, std::extents<signed char, 5>());
+    TEST_LIBCPP_ASSERT_FAILURE(m[-1], "mdspan: operator[] out of bounds access");
+    TEST_LIBCPP_ASSERT_FAILURE(m[-130], "mdspan: operator[] out of bounds access");
+    TEST_LIBCPP_ASSERT_FAILURE(m[5], "mdspan: operator[] out of bounds access");
+    TEST_LIBCPP_ASSERT_FAILURE(m[1000], "mdspan: operator[] out of bounds access");
+  }
+  {
+    std::mdspan m(data, std::dextents<unsigned char, 1>(5));
+    TEST_LIBCPP_ASSERT_FAILURE(m[-1], "mdspan: operator[] out of bounds access");
+    TEST_LIBCPP_ASSERT_FAILURE(m[-130], "mdspan: operator[] out of bounds access");
+    TEST_LIBCPP_ASSERT_FAILURE(m[5], "mdspan: operator[] out of bounds access");
+    TEST_LIBCPP_ASSERT_FAILURE(m[1000], "mdspan: operator[] out of bounds access");
+  }
+  {
+    std::mdspan m(data, std::dextents<signed char, 1>(5));
+    TEST_LIBCPP_ASSERT_FAILURE(m[-1], "mdspan: operator[] out of bounds access");
+    TEST_LIBCPP_ASSERT_FAILURE(m[-130], "mdspan: operator[] out of bounds access");
+    TEST_LIBCPP_ASSERT_FAILURE(m[5], "mdspan: operator[] out of bounds access");
+    TEST_LIBCPP_ASSERT_FAILURE(m[1000], "mdspan: operator[] out of bounds access");
+  }
+  {
+    std::mdspan m(data, std::dextents<int, 3>(5, 7, 9));
+    TEST_LIBCPP_ASSERT_FAILURE((m[-1, -1, -1]), "mdspan: operator[] out of bounds access");
+    TEST_LIBCPP_ASSERT_FAILURE((m[-1, 0, 0]), "mdspan: operator[] out of bounds access");
+    TEST_LIBCPP_ASSERT_FAILURE((m[0, -1, 0]), "mdspan: operator[] out of bounds access");
+    TEST_LIBCPP_ASSERT_FAILURE((m[0, 0, -1]), "mdspan: operator[] out of bounds access");
+    TEST_LIBCPP_ASSERT_FAILURE((m[5, 3, 3]), "mdspan: operator[] out of bounds access");
+    TEST_LIBCPP_ASSERT_FAILURE((m[3, 7, 3]), "mdspan: operator[] out of bounds access");
+    TEST_LIBCPP_ASSERT_FAILURE((m[3, 3, 9]), "mdspan: operator[] out of bounds access");
+    TEST_LIBCPP_ASSERT_FAILURE((m[5, 7, 9]), "mdspan: operator[] out of bounds access");
+  }
+  {
+    std::mdspan m(data, std::dextents<unsigned, 3>(5, 7, 9));
+    TEST_LIBCPP_ASSERT_FAILURE((m[-1, -1, -1]), "mdspan: operator[] out of bounds access");
+    TEST_LIBCPP_ASSERT_FAILURE((m[-1, 0, 0]), "mdspan: operator[] out of bounds access");
+    TEST_LIBCPP_ASSERT_FAILURE((m[0, -1, 0]), "mdspan: operator[] out of bounds access");
+    TEST_LIBCPP_ASSERT_FAILURE((m[0, 0, -1]), "mdspan: operator[] out of bounds access");
+    TEST_LIBCPP_ASSERT_FAILURE((m[5, 3, 3]), "mdspan: operator[] out of bounds access");
+    TEST_LIBCPP_ASSERT_FAILURE((m[3, 7, 3]), "mdspan: operator[] out of bounds access");
+    TEST_LIBCPP_ASSERT_FAILURE((m[3, 3, 9]), "mdspan: operator[] out of bounds access");
+    TEST_LIBCPP_ASSERT_FAILURE((m[5, 7, 9]), "mdspan: operator[] out of bounds access");
+  }
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/views/mdspan/mdspan/assert.size.pass.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/assert.size.pass.cpp
new file mode 100644
index 00000000000000..0266ec11c21f5f
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/assert.size.pass.cpp
@@ -0,0 +1,50 @@
+//
+// 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-has-debug-mode
+// XFAIL: availability-verbose_abort-missing
+
+// <mdspan>
+
+// constexpr size_type size() const noexcept;
+//
+// Preconditions: The size of the multidimensional index space extents() is representable as a value of type size_type ([basic.fundamental]).
+//
+// Returns: extents().fwd-prod-of-extents(rank()).
+
+#include <array>
+#include <cassert>
+#include <mdspan>
+
+#include "check_assertion.h"
+#include "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**) {
+  std::array<float, 10> data;
+  // make sure we are not failing because of using index_type instead of size_type
+  {
+    typename layout_wrapping_integral<4>::template mapping<std::dextents<char, 2>> map(
+      std::dextents<char, 2>(100, 2), not_extents_constructible_tag());
+    std::mdspan<float, std::dextents<char, 2>, layout_wrapping_integral<4>> mds(data.data(), map);
+    assert(map.required_span_size() == char(8));
+    assert((static_cast<unsigned char>(200) == mds.size()));
+  }
+  {
+    typename layout_wrapping_integral<4>::template mapping<std::dextents<char, 2>> map(
+      std::dextents<char, 2>(100, 3), not_extents_constructible_tag());
+    std::mdspan<float, std::dextents<char, 2>, layout_wrapping_integral<4>> mds(data.data(), map);
+    // sanity check
+    assert(map.required_span_size() == char(12));
+    // 100 x 3 exceeds 256
+    {
+      TEST_LIBCPP_ASSERT_FAILURE(([=] { mds.size(); }()), "mdspan: size() is not representable as size_type");
+    }
+  }
+  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
new file mode 100644
index 00000000000000..a6f436fd44880c
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/assign.pass.cpp
@@ -0,0 +1,110 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 mdspan& operator=(const mdspan& rhs) = default;
+
+#include <mdspan>
+#include <type_traits>
+#include <concepts>
+#include <cassert>
+
+#include "test_macros.h"
+
+#include "../MinimalElementType.h"
+#include "CustomTestLayouts.h"
+#include "CustomTestAccessors.h"
+
+template <class H, class M, class A>
+constexpr void test_mdspan_types(const H& handle, const M& map, const A& acc) {
+  using MDS = std::mdspan<typename A::element_type, typename M::extents_type, typename M::layout_type, A>;
+
+  MDS m_org(handle, map, acc);
+  MDS m(handle, map, acc);
+
+  // The defaulted assignment operator seems to be deprecated because:
+  //   error: definition of implicit copy assignment operator for 'checked_accessor<const double>' is deprecated
+  //   because it has a user-provided copy constructor [-Werror,-Wdeprecated-copy-with-user-provided-copy]
+  if constexpr (!std::is_same_v<A, checked_accessor<const double>>)
+    m = m_org;
+  // even though the following checks out:
+  static_assert(std::copyable<checked_accessor<const double>>);
+  static_assert(std::is_assignable_v<checked_accessor<const double>, checked_accessor<const double>>);
+
+  static_assert(noexcept(m = m_org));
+  assert(m.extents() == map.extents());
+  if constexpr (std::equality_comparable<H>)
+    assert(m.data_handle() == handle);
+  if constexpr (std::equality_comparable<M>)
+    assert(m.mapping() == map);
+  if constexpr (std::equality_comparable<A>)
+    assert(m.accessor() == acc);
+
+  static_assert(std::is_trivially_assignable_v<MDS, const MDS&> ==
+                ((!std::is_class_v<H> ||
+                  std::is_trivially_assignable_v<H, const H&>)&&std::is_trivially_assignable_v<M, const M&> &&
+                 std::is_trivially_assignable_v<A, const A&>));
+}
+
+template <class H, class L, class A>
+constexpr void mixin_extents(const H& handle, const L& layout, const A& acc) {
+  constexpr size_t D = std::dynamic_extent;
+  test_mdspan_types(handle, construct_mapping(layout, std::extents<int>()), acc);
+  test_mdspan_types(handle, construct_mapping(layout, std::extents<char, D>(7)), acc);
+  test_mdspan_types(handle, construct_mapping(layout, std::extents<unsigned, 7>()), acc);
+  test_mdspan_types(handle, construct_mapping(layout, std::extents<size_t, D, 4, D>(2, 3)), acc);
+  test_mdspan_types(handle, construct_mapping(layout, std::extents<char, D, 7, D>(0, 3)), acc);
+  test_mdspan_types(handle, construct_mapping(layout, std::extents<int64_t, D, 7, D, 4, D, D>(1, 2, 3, 2)), acc);
+}
+
+template <class H, class A>
+constexpr void mixin_layout(const H& handle, const A& acc) {
+  // make sure we test a trivially assignable mapping
+  static_assert(std::is_trivially_assignable_v<typename std::layout_left::template mapping<std::extents<int>>,
+                                               const typename std::layout_left::template mapping<std::extents<int>>&>);
+  mixin_extents(handle, std::layout_left(), acc);
+  mixin_extents(handle, std::layout_right(), acc);
+  // make sure we test a not trivially assignable mapping
+  static_assert(!std::is_trivially_assignable_v<
+                typename layout_wrapping_integral<4>::template mapping<std::extents<int>>,
+                const typename layout_wrapping_integral<4>::template mapping<std::extents<int>>&>);
+  mixin_extents(handle, layout_wrapping_integral<4>(), acc);
+}
+
+template <class T>
+constexpr void mixin_accessor() {
+  ElementPool<T, 1024> elements;
+  // make sure we test trivially constructible accessor and data_handle
+  static_assert(std::is_trivially_copyable_v<std::default_accessor<T>>);
+  static_assert(std::is_trivially_copyable_v<typename std::default_accessor<T>::data_handle_type>);
+  mixin_layout(elements.get_ptr(), std::default_accessor<T>());
+
+  // Using weird accessor/data_handle
+  // Make sure they actually got the properties we want to test
+  // checked_accessor is noexcept copy constructible except for const double
+  checked_accessor<T> acc(1024);
+  static_assert(noexcept(checked_accessor<T>(acc)) != std::is_same_v<T, const double>);
+  mixin_layout(typename checked_accessor<T>::data_handle_type(elements.get_ptr()), acc);
+}
+
+constexpr bool test() {
+  mixin_accessor<int>();
+  mixin_accessor<const int>();
+  mixin_accessor<double>();
+  mixin_accessor<const double>();
+  mixin_accessor<MinimalElementType>();
+  mixin_accessor<const MinimalElementType>();
+  return true;
+}
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/views/mdspan/mdspan/conversion.pass.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/conversion.pass.cpp
new file mode 100644
index 00000000000000..bb592763335032
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/conversion.pass.cpp
@@ -0,0 +1,281 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 OtherElementType, class OtherExtents,
+//         class OtherLayoutPolicy, class OtherAccessor>
+//  constexpr explicit(see below)
+//    mdspan(const mdspan<OtherElementType, OtherExtents,
+//                        OtherLayoutPolicy, OtherAccessor>& other);
+//
+// Constraints:
+//   - is_constructible_v<mapping_type, const OtherLayoutPolicy::template mapping<OtherExtents>&> is true, and
+//   - is_constructible_v<accessor_type, const OtherAccessor&> is true.
+// Mandates:
+//   - is_constructible_v<data_handle_type, const OtherAccessor::data_handle_type&> is
+//   - is_constructible_v<extents_type, OtherExtents> is true.
+//
+// Preconditions:
+//   - For each rank index r of extents_type, static_extent(r) == dynamic_extent || static_extent(r) == other.extent(r) is true.
+//   - [0, map_.required_span_size()) is an accessible range of ptr_ and acc_ for values of ptr_, map_, and acc_ after the invocation of this constructor.
+//
+// Effects:
+//   - Direct-non-list-initializes ptr_ with other.ptr_,
+//   - direct-non-list-initializes map_ with other.map_, and
+//   - direct-non-list-initializes acc_ with other.acc_.
+//
+// Remarks: The expression inside explicit is equivalent to:
+//   !is_convertible_v<const OtherLayoutPolicy::template mapping<OtherExtents>&, mapping_type>
+//   || !is_convertible_v<const OtherAccessor&, accessor_type>
+
+#include <mdspan>
+#include <type_traits>
+#include <concepts>
+#include <cassert>
+
+#include "test_macros.h"
+
+#include "../MinimalElementType.h"
+#include "CustomTestLayouts.h"
+#include "CustomTestAccessors.h"
+
+template <class ToMDS, class FromMDS>
+constexpr void test_implicit_conversion(ToMDS to_mds, FromMDS from_mds) {
+  assert(to_mds.extents() == from_mds.extents());
+  if constexpr (std::equality_comparable_with<typename ToMDS::data_handle_type, typename FromMDS::data_handle_type>)
+    assert(to_mds.data_handle() == from_mds.data_handle());
+  if constexpr (std::equality_comparable_with<typename ToMDS::mapping_type, typename FromMDS::mapping_type>)
+    assert(to_mds.mapping() == from_mds.mapping());
+  if constexpr (std::equality_comparable_with<typename ToMDS::accessor_type, typename FromMDS::accessor_type>)
+    assert(to_mds.accessor() == from_mds.accessor());
+}
+
+template <class M>
+concept mapping_requirements = requires() {
+  requires(std::copyable<M> && std::equality_comparable<M>) && std::is_nothrow_move_constructible_v<M> &&
+              std::is_nothrow_move_assignable_v<M> && std::is_nothrow_swappable_v<M>;
+};
+
+template <class ToMDS, class FromMDS>
+constexpr void test_conversion(FromMDS from_mds) {
+  // check some requirements, to see we didn't screw up our test layouts/accessors
+  static_assert(mapping_requirements<typename ToMDS::mapping_type>);
+  static_assert(mapping_requirements<typename FromMDS::mapping_type>);
+
+  constexpr bool constructible =
+      std::is_constructible_v<typename ToMDS::mapping_type, const typename FromMDS::mapping_type&> &&
+      std::is_constructible_v<typename ToMDS::accessor_type, const typename FromMDS::accessor_type&>;
+  constexpr bool convertible =
+      std::is_convertible_v<const typename FromMDS::mapping_type&, typename ToMDS::mapping_type> &&
+      std::is_convertible_v<const typename FromMDS::accessor_type&, typename ToMDS::accessor_type>;
+  constexpr bool passes_mandates =
+      std::is_constructible_v<typename ToMDS::data_handle_type, const typename FromMDS::data_handle_type&> &&
+      std::is_constructible_v<typename ToMDS::extents_type, typename FromMDS::extents_type>;
+
+  if constexpr (constructible) {
+    if constexpr (passes_mandates) {
+      ToMDS to_mds(from_mds);
+      assert(to_mds.extents() == from_mds.extents());
+      if constexpr (std::equality_comparable_with<typename ToMDS::data_handle_type, typename FromMDS::data_handle_type>)
+        assert(to_mds.data_handle() == from_mds.data_handle());
+      if constexpr (std::equality_comparable_with<typename ToMDS::mapping_type, typename FromMDS::mapping_type>)
+        assert(to_mds.mapping() == from_mds.mapping());
+      if constexpr (std::equality_comparable_with<typename ToMDS::accessor_type, typename FromMDS::accessor_type>)
+        assert(to_mds.accessor() == from_mds.accessor());
+      if constexpr (convertible) {
+        test_implicit_conversion(from_mds, from_mds);
+      } else {
+        static_assert(!std::is_convertible_v<FromMDS, ToMDS>);
+      }
+    }
+  } else {
+    static_assert(!std::is_constructible_v<ToMDS, FromMDS>);
+  }
+}
+
+template <class ToL, class ToE, class ToA, class FromH, class FromL, class FromE, class FromA>
+constexpr void construct_from_mds(const FromH& handle, const FromL& layout, const FromE& exts, const FromA& acc) {
+  using ToMDS   = std::mdspan<typename ToA::element_type, ToE, ToL, ToA>;
+  using FromMDS = std::mdspan<typename FromA::element_type, FromE, FromL, FromA>;
+  test_conversion<ToMDS>(FromMDS(handle, construct_mapping(layout, exts), acc));
+}
+
+template <class ToL, class ToA, class FromH, class FromL, class FromA>
+constexpr void mixin_extents(const FromH& handle, const FromL& layout, const FromA& acc) {
+  constexpr size_t D = std::dynamic_extent;
+  // constructible and convertible
+  construct_from_mds<ToL, std::dextents<int, 0>, ToA>(handle, layout, std::dextents<int, 0>(), acc);
+  construct_from_mds<ToL, std::dextents<int, 1>, ToA>(handle, layout, std::dextents<int, 1>(4), acc);
+  construct_from_mds<ToL, std::dextents<int, 1>, ToA>(handle, layout, std::extents<int, 4>(), acc);
+  construct_from_mds<ToL, std::dextents<int, 2>, ToA>(handle, layout, std::dextents<int, 2>(4, 5), acc);
+  construct_from_mds<ToL, std::dextents<unsigned, 2>, ToA>(handle, layout, std::dextents<int, 2>(4, 5), acc);
+  construct_from_mds<ToL, std::dextents<unsigned, 2>, ToA>(handle, layout, std::extents<int, D, 5>(4), acc);
+  construct_from_mds<ToL, std::extents<int, D, 5>, ToA>(handle, layout, std::extents<int, D, 5>(4), acc);
+  construct_from_mds<ToL, std::extents<int, D, 5>, ToA>(handle, layout, std::extents<int, D, 5>(4), acc);
+  construct_from_mds<ToL, std::extents<int, D, 5, D, 7>, ToA>(handle, layout, std::extents<int, D, 5, D, 7>(4, 6), acc);
+
+  // not convertible
+  construct_from_mds<ToL, std::dextents<int, 1>, ToA>(handle, layout, std::dextents<unsigned, 1>(4), acc);
+  construct_from_mds<ToL, std::extents<int, D, 5, D, 7>, ToA>(
+      handle, layout, std::extents<int, D, 5, D, D>(4, 6, 7), acc);
+
+  // not constructible
+  construct_from_mds<ToL, std::dextents<int, 1>, ToA>(handle, layout, std::dextents<int, 2>(4, 5), acc);
+  construct_from_mds<ToL, std::extents<int, D, 5, D, 8>, ToA>(handle, layout, std::extents<int, D, 5, D, 7>(4, 6), acc);
+}
+
+template <class ToA, class FromH, class FromA>
+constexpr void mixin_layout(const FromH& handle, const FromA& acc) {
+  mixin_extents<std::layout_left, ToA>(handle, std::layout_left(), acc);
+  mixin_extents<std::layout_right, ToA>(handle, std::layout_right(), acc);
+  // Check layout policy conversion
+  // 
diff erent layout policies, but constructible and convertible
+  static_assert(std::is_constructible_v<std::layout_left::mapping<std::dextents<int, 1>>,
+                                        const std::layout_right::mapping<std::dextents<int, 1>>&>);
+  static_assert(std::is_convertible_v<const std::layout_right::mapping<std::dextents<int, 1>>&,
+                                      std::layout_left::mapping<std::dextents<int, 1>>>);
+  // 
diff erent layout policies, not constructible
+  static_assert(!std::is_constructible_v<std::layout_left::mapping<std::dextents<int, 2>>,
+                                         const std::layout_right::mapping<std::dextents<int, 2>>&>);
+  // 
diff erent layout policies, constructible and not convertible
+  static_assert(std::is_constructible_v<std::layout_left::mapping<std::dextents<int, 1>>,
+                                        const std::layout_right::mapping<std::dextents<size_t, 1>>&>);
+  static_assert(!std::is_convertible_v<const std::layout_right::mapping<std::dextents<size_t, 1>>&,
+                                       std::layout_left::mapping<std::dextents<int, 1>>>);
+
+  mixin_extents<std::layout_left, ToA>(handle, std::layout_right(), acc);
+  mixin_extents<layout_wrapping_integral<4>, ToA>(handle, layout_wrapping_integral<4>(), acc);
+  // 
diff erent layout policies, constructible and not convertible
+  static_assert(!std::is_constructible_v<layout_wrapping_integral<8>::mapping<std::dextents<unsigned, 2>>,
+                                         const layout_wrapping_integral<8>::mapping<std::dextents<int, 2>>&>);
+  static_assert(std::is_constructible_v<layout_wrapping_integral<8>::mapping<std::dextents<unsigned, 2>>,
+                                        layout_wrapping_integral<8>::mapping<std::dextents<int, 2>>>);
+  mixin_extents<layout_wrapping_integral<8>, ToA>(handle, layout_wrapping_integral<8>(), acc);
+}
+
+// check that we cover all corners with respect to constructibility and convertibility
+template <bool constructible_constref_acc,
+          bool convertible_constref_acc,
+          bool constructible_nonconst_acc,
+          bool convertible_nonconst_acc,
+          bool constructible_constref_handle,
+          bool convertible_constref_handle,
+          bool constructible_nonconst_handle,
+          bool convertible_nonconst_handle,
+          class ToA,
+          class FromA>
+constexpr bool test(FromA from_acc) {
+  static_assert(std::copyable<ToA>);
+  static_assert(std::copyable<FromA>);
+  static_assert(std::is_constructible_v<ToA, const FromA&> == constructible_constref_acc);
+  static_assert(std::is_constructible_v<ToA, FromA> == constructible_nonconst_acc);
+  static_assert(std::is_constructible_v<typename ToA::data_handle_type, const typename FromA::data_handle_type&> ==
+                constructible_constref_handle);
+  static_assert(std::is_constructible_v<typename ToA::data_handle_type, typename FromA::data_handle_type> ==
+                constructible_nonconst_handle);
+  static_assert(std::is_convertible_v<const FromA&, ToA> == convertible_constref_acc);
+  static_assert(std::is_convertible_v<FromA, ToA> == convertible_nonconst_acc);
+  static_assert(std::is_convertible_v<const typename FromA::data_handle_type&, typename ToA::data_handle_type> ==
+                convertible_constref_handle);
+  static_assert(std::is_convertible_v<typename FromA::data_handle_type, typename ToA::data_handle_type> ==
+                convertible_nonconst_handle);
+
+  ElementPool<typename FromA::element_type, 1024> elements;
+  mixin_layout<ToA>(typename FromA::data_handle_type(elements.get_ptr()), from_acc);
+  return true;
+}
+
+int main(int, char**) {
+  // using shorthands here: t and o for better visual distinguishability
+  constexpr bool t = true;
+  constexpr bool o = false;
+
+  // possibility matrix for constructibility and convertibility https://godbolt.org/z/98KGo8Wbc
+  // you can't have convertibility without constructibility
+  // and if you take const T& then you also can take T
+  // this leaves 7 combinations
+  // const_ref_ctor, const_ref_conv, nonconst_ctor, nonconst_conv, tested
+  // o o o o X
+  // o o t o X
+  // o o t t X
+  // t o t o X
+  // t o t t X
+  // t t t o X
+  // t t t t X
+
+  // checked_accessor has various weird data handles and some weird conversion properties
+  // conv_test_accessor_c/nc is an accessor pair which has configurable conversion properties, but plain ptr as data handle
+  // accessor constructible
+  test<t, t, t, t, t, t, t, t, std::default_accessor<float>>(std::default_accessor<float>());
+  test<t, t, t, t, t, t, t, t, std::default_accessor<const float>>(std::default_accessor<float>());
+  test<t, t, t, t, t, t, t, t, std::default_accessor<MinimalElementType>>(std::default_accessor<MinimalElementType>());
+  test<t, t, t, t, t, t, t, t, std::default_accessor<const MinimalElementType>>(
+      std::default_accessor<MinimalElementType>());
+  test<t, t, t, t, t, t, t, t, checked_accessor<int>>(checked_accessor<int>(1024));
+  test<t, o, t, o, t, t, t, t, checked_accessor<const int>>(checked_accessor<int>(1024));
+  test<t, t, t, t, o, o, o, o, checked_accessor<const unsigned>>(checked_accessor<unsigned>(1024));
+  test<t, t, t, t, t, t, t, t, checked_accessor<float>>(checked_accessor<float>(1024));
+  test<t, t, t, t, t, t, t, t, checked_accessor<double>>(checked_accessor<double>(1024));
+  test<t, t, t, t, t, t, t, t, checked_accessor<MinimalElementType>>(checked_accessor<MinimalElementType>(1024));
+  test<t, o, t, o, t, t, t, t, checked_accessor<const MinimalElementType>>(checked_accessor<MinimalElementType>(1024));
+  test<t, o, t, o, t, t, t, t, conv_test_accessor_c<int, t, t, t, t>>(conv_test_accessor_nc<int, t, t>());
+  test<t, o, t, t, t, t, t, t, conv_test_accessor_c<int, t, t, o, o>>(conv_test_accessor_nc<int, t, o>());
+  // FIXME: these tests trigger what appears to be a compiler bug on MINGW32 with --target=x86_64-w64-windows-gnu
+  // https://godbolt.org/z/KK8aj5bs7
+  // Bug report: https://github.com/llvm/llvm-project/issues/64077
+  #ifndef __MINGW32__
+  test<t, t, t, o, t, t, t, t, conv_test_accessor_c<int, o, t, t, t>>(conv_test_accessor_nc<int, t, t>());
+  test<t, t, t, t, t, t, t, t, conv_test_accessor_c<int, o, o, o, o>>(conv_test_accessor_nc<int, t, o>());
+  #endif
+
+  // ElementType convertible, but accessor not constructible
+  test<o, o, o, o, o, o, o, o, std::default_accessor<float>>(std::default_accessor<int>());
+  test<o, o, o, o, o, o, o, o, checked_accessor<const double>>(checked_accessor<double>(1024));
+  test<o, o, t, t, t, t, t, t, checked_accessor<const float>>(checked_accessor<float>(1024));
+  test<o, o, o, o, t, t, t, t, conv_test_accessor_c<int, o, o, t, t>>(conv_test_accessor_nc<int, o, o>());
+  test<o, o, t, o, t, t, t, t, conv_test_accessor_c<int, o, t, o, o>>(conv_test_accessor_nc<int, o, t>());
+  test<o, o, t, t, t, t, t, t, conv_test_accessor_c<int, o, o, t, t>>(conv_test_accessor_nc<int, o, t>());
+
+  // Ran into trouble with doing it all in one static_assert: exceeding step limit for consteval
+  static_assert(test<t, t, t, t, t, t, t, t, std::default_accessor<float>>(std::default_accessor<float>()));
+  static_assert(test<t, t, t, t, t, t, t, t, std::default_accessor<const float>>(std::default_accessor<float>()));
+  static_assert(test<t, t, t, t, t, t, t, t, std::default_accessor<MinimalElementType>>(
+      std::default_accessor<MinimalElementType>()));
+  static_assert(test<t, t, t, t, t, t, t, t, std::default_accessor<const MinimalElementType>>(
+      std::default_accessor<MinimalElementType>()));
+  static_assert(test<t, t, t, t, t, t, t, t, checked_accessor<int>>(checked_accessor<int>(1024)));
+  static_assert(test<t, o, t, o, t, t, t, t, checked_accessor<const int>>(checked_accessor<int>(1024)));
+  static_assert(test<t, t, t, t, o, o, o, o, checked_accessor<const unsigned>>(checked_accessor<unsigned>(1024)));
+  static_assert(test<t, t, t, t, t, t, t, t, checked_accessor<float>>(checked_accessor<float>(1024)));
+  static_assert(test<t, t, t, t, t, t, t, t, checked_accessor<double>>(checked_accessor<double>(1024)));
+  static_assert(
+      test<t, t, t, t, t, t, t, t, checked_accessor<MinimalElementType>>(checked_accessor<MinimalElementType>(1024)));
+  static_assert(test<t, o, t, o, t, t, t, t, checked_accessor<const MinimalElementType>>(
+      checked_accessor<MinimalElementType>(1024)));
+  static_assert(
+      test<t, o, t, o, t, t, t, t, conv_test_accessor_c<int, t, t, t, t>>(conv_test_accessor_nc<int, t, t>()));
+  static_assert(
+      test<t, o, t, t, t, t, t, t, conv_test_accessor_c<int, t, t, o, o>>(conv_test_accessor_nc<int, t, o>()));
+  static_assert(
+      test<t, t, t, o, t, t, t, t, conv_test_accessor_c<int, o, t, t, t>>(conv_test_accessor_nc<int, t, t>()));
+  static_assert(
+      test<t, t, t, t, t, t, t, t, conv_test_accessor_c<int, o, o, o, o>>(conv_test_accessor_nc<int, t, o>()));
+  static_assert(test<o, o, o, o, o, o, o, o, std::default_accessor<float>>(std::default_accessor<int>()));
+  static_assert(test<o, o, o, o, o, o, o, o, checked_accessor<const double>>(checked_accessor<double>(1024)));
+  static_assert(test<o, o, t, t, t, t, t, t, checked_accessor<const float>>(checked_accessor<float>(1024)));
+  static_assert(
+      test<o, o, o, o, t, t, t, t, conv_test_accessor_c<int, o, o, t, t>>(conv_test_accessor_nc<int, o, o>()));
+  static_assert(
+      test<o, o, t, o, t, t, t, t, conv_test_accessor_c<int, o, t, o, o>>(conv_test_accessor_nc<int, o, t>()));
+  static_assert(
+      test<o, o, t, t, t, t, t, t, conv_test_accessor_c<int, o, o, t, t>>(conv_test_accessor_nc<int, o, t>()));
+
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/views/mdspan/mdspan/conversion.verify.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/conversion.verify.cpp
new file mode 100644
index 00000000000000..5a0ece29a9b84c
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/conversion.verify.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>
+
+// template<class OtherElementType, class OtherExtents,
+//         class OtherLayoutPolicy, class OtherAccessor>
+//  constexpr explicit(see below)
+//    mdspan(const mdspan<OtherElementType, OtherExtents,
+//                        OtherLayoutPolicy, OtherAccessor>& other);
+//
+// Constraints:
+//   - is_constructible_v<mapping_type, const OtherLayoutPolicy::template mapping<OtherExtents>&> is true, and
+//   - is_constructible_v<accessor_type, const OtherAccessor&> is true.
+// Mandates:
+//   - is_constructible_v<data_handle_type, const OtherAccessor::data_handle_type&> is
+//   - is_constructible_v<extents_type, OtherExtents> is true.
+//
+// Preconditions:
+//   - For each rank index r of extents_type, static_extent(r) == dynamic_extent || static_extent(r) == other.extent(r) is true.
+//   - [0, map_.required_span_size()) is an accessible range of ptr_ and acc_ for values of ptr_, map_, and acc_ after the invocation of this constructor.
+//
+// Effects:
+//   - Direct-non-list-initializes ptr_ with other.ptr_,
+//   - direct-non-list-initializes map_ with other.map_, and
+//   - direct-non-list-initializes acc_ with other.acc_.
+//
+// Remarks: The expression inside explicit is equivalent to:
+//   !is_convertible_v<const OtherLayoutPolicy::template mapping<OtherExtents>&, mapping_type>
+//   || !is_convertible_v<const OtherAccessor&, accessor_type>
+
+#include <mdspan>
+#include "CustomTestAccessors.h"
+#include "CustomTestLayouts.h"
+
+void cant_construct_data_handle_type() {
+  int data;
+  std::mdspan<int, std::extents<int>, std::layout_right, convertible_accessor_but_not_handle<int>> m_nc(&data);
+  // expected-error-re@*:* {{{{.*}}no matching constructor for initialization of {{.*}} (aka 'not_const_convertible_handle<const int>')}}
+  // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}mdspan: incompatible data_handle_type for mdspan construction}}
+  [[maybe_unused]] std::
+      mdspan<const int, std::extents<int>, std::layout_right, convertible_accessor_but_not_handle<const int>>
+          m_c(m_nc);
+}
+
+void mapping_constructible_despite_extents_compatibility() {
+  int data;
+  std::mdspan<int, std::extents<int>, always_convertible_layout> m(&data);
+  // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}mdspan: incompatible extents for mdspan construction}}
+  [[maybe_unused]] std::mdspan<int, std::extents<int, 5>, always_convertible_layout> m2(m);
+}

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
new file mode 100644
index 00000000000000..813250d8cefff5
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/ctor.copy.pass.cpp
@@ -0,0 +1,98 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 mdspan(const mdspan&) = default;
+//
+// A specialization of mdspan is a trivially copyable type if its accessor_type, mapping_type, and data_handle_type are trivially copyable types.
+
+#include <mdspan>
+#include <type_traits>
+#include <concepts>
+#include <cassert>
+
+#include "test_macros.h"
+
+#include "../MinimalElementType.h"
+#include "CustomTestLayouts.h"
+#include "CustomTestAccessors.h"
+
+template <class H, class M, class A>
+constexpr void test_mdspan_types(const H& handle, const M& map, const A& acc) {
+  using MDS = std::mdspan<typename A::element_type, typename M::extents_type, typename M::layout_type, A>;
+
+  MDS m_org(handle, map, acc);
+  MDS m(m_org);
+  static_assert(noexcept(MDS(m_org)) == (noexcept(H(handle))&& noexcept(M(map))&& noexcept(A(acc))));
+  static_assert(
+      std::is_trivially_copyable_v<MDS> ==
+      (std::is_trivially_copyable_v<H> && std::is_trivially_copyable_v<M> && std::is_trivially_copyable_v<A>));
+  assert(m.extents() == map.extents());
+  if constexpr (std::equality_comparable<H>)
+    assert(m.data_handle() == handle);
+  if constexpr (std::equality_comparable<M>)
+    assert(m.mapping() == map);
+  if constexpr (std::equality_comparable<A>)
+    assert(m.accessor() == acc);
+}
+
+template <class H, class L, class A>
+constexpr void mixin_extents(const H& handle, const L& layout, const A& acc) {
+  constexpr size_t D = std::dynamic_extent;
+  test_mdspan_types(handle, construct_mapping(layout, std::extents<int>()), acc);
+  test_mdspan_types(handle, construct_mapping(layout, std::extents<char, D>(7)), acc);
+  test_mdspan_types(handle, construct_mapping(layout, std::extents<unsigned, 7>()), acc);
+  test_mdspan_types(handle, construct_mapping(layout, std::extents<size_t, D, 4, D>(2, 3)), acc);
+  test_mdspan_types(handle, construct_mapping(layout, std::extents<char, D, 7, D>(0, 3)), acc);
+  test_mdspan_types(handle, construct_mapping(layout, std::extents<int64_t, D, 7, D, 4, D, D>(1, 2, 3, 2)), acc);
+}
+
+template <class H, class A>
+constexpr void mixin_layout(const H& handle, const A& acc) {
+  // make sure we test a trivially copyable mapping
+  static_assert(std::is_trivially_copyable_v<typename std::layout_left::template mapping<std::extents<int>>>);
+  mixin_extents(handle, std::layout_left(), acc);
+  mixin_extents(handle, std::layout_right(), acc);
+  // make sure we test a not trivially copyable mapping
+  static_assert(
+      !std::is_trivially_copyable_v<typename layout_wrapping_integral<4>::template mapping<std::extents<int>>>);
+  mixin_extents(handle, layout_wrapping_integral<4>(), acc);
+}
+
+template <class T>
+constexpr void mixin_accessor() {
+  ElementPool<T, 1024> elements;
+  // make sure we test trivially constructible accessor and data_handle
+  static_assert(std::is_trivially_copyable_v<std::default_accessor<T>>);
+  static_assert(std::is_trivially_copyable_v<typename std::default_accessor<T>::data_handle_type>);
+  mixin_layout(elements.get_ptr(), std::default_accessor<T>());
+
+  // Using weird accessor/data_handle
+  // Make sure they actually got the properties we want to test
+  // checked_accessor is noexcept copy constructible except for const double
+  checked_accessor<T> acc(1024);
+  static_assert(noexcept(checked_accessor<T>(acc)) != std::is_same_v<T, const double>);
+  mixin_layout(typename checked_accessor<T>::data_handle_type(elements.get_ptr()), acc);
+}
+
+constexpr bool test() {
+  mixin_accessor<int>();
+  mixin_accessor<const int>();
+  mixin_accessor<double>();
+  mixin_accessor<const double>();
+  mixin_accessor<MinimalElementType>();
+  mixin_accessor<const MinimalElementType>();
+  return true;
+}
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}

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
new file mode 100644
index 00000000000000..f42a4dbf1bf2c0
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/ctor.default.pass.cpp
@@ -0,0 +1,111 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 mdspan();
+// Constraints:
+//   - rank_dynamic() > 0 is true.
+//   - is_default_constructible_v<data_handle_type> is true.
+//   - is_default_constructible_v<mapping_type> is true.
+//   - is_default_constructible_v<accessor_type> is true.
+//
+// Preconditions: [0, map_.required_span_size()) is an accessible range of ptr_
+//               and acc_ for the values of map_ and acc_ after the invocation of this constructor.
+//
+// Effects: Value-initializes ptr_, map_, and acc_.
+
+#include <mdspan>
+#include <type_traits>
+#include <concepts>
+#include <cassert>
+
+#include "test_macros.h"
+
+#include "../MinimalElementType.h"
+#include "CustomTestLayouts.h"
+#include "CustomTestAccessors.h"
+
+template <bool hc, bool mc, bool ac, class H, class M, class A>
+constexpr void test_mdspan_types(const H&, const M&, const A&) {
+  using MDS = std::mdspan<typename A::element_type, typename M::extents_type, typename M::layout_type, A>;
+
+  static_assert(hc == std::is_default_constructible_v<H>);
+  static_assert(mc == std::is_default_constructible_v<M>);
+  static_assert(ac == std::is_default_constructible_v<A>);
+
+  if constexpr (MDS::rank_dynamic() > 0 && hc && mc && ac) {
+    MDS m;
+    static_assert(noexcept(MDS()) == (noexcept(H())&& noexcept(M())&& noexcept(A())));
+    assert(m.extents() == typename MDS::extents_type());
+    if constexpr (std::equality_comparable<H>)
+      assert(m.data_handle() == H());
+    if constexpr (std::equality_comparable<M>)
+      assert(m.mapping() == M());
+    if constexpr (std::equality_comparable<A>)
+      assert(m.accessor() == A());
+  } else {
+    static_assert(!std::is_default_constructible_v<MDS>);
+  }
+}
+
+template <bool hc, bool mc, bool ac, class H, class L, class A>
+constexpr void mixin_extents(const H& handle, const L& layout, const A& acc) {
+  constexpr size_t D = std::dynamic_extent;
+  test_mdspan_types<hc, mc, ac>(handle, construct_mapping(layout, std::extents<int>()), acc);
+  test_mdspan_types<hc, mc, ac>(handle, construct_mapping(layout, std::extents<char, D>(7)), acc);
+  test_mdspan_types<hc, mc, ac>(handle, construct_mapping(layout, std::extents<unsigned, 7>()), acc);
+  test_mdspan_types<hc, mc, ac>(handle, construct_mapping(layout, std::extents<size_t, D, 4, D>(2, 3)), acc);
+  test_mdspan_types<hc, mc, ac>(handle, construct_mapping(layout, std::extents<char, D, 7, D>(0, 3)), acc);
+  test_mdspan_types<hc, mc, ac>(
+      handle, construct_mapping(layout, std::extents<int64_t, D, 7, D, 4, D, D>(1, 2, 3, 2)), acc);
+}
+
+template <bool hc, bool ac, class H, class A>
+constexpr void mixin_layout(const H& handle, const A& acc) {
+  mixin_extents<hc, true, ac>(handle, std::layout_left(), acc);
+  mixin_extents<hc, true, ac>(handle, std::layout_right(), acc);
+
+  // Use weird layout, make sure it has the properties we want to test
+  constexpr size_t D = std::dynamic_extent;
+  static_assert(
+      !std::is_default_constructible_v< typename layout_wrapping_integral<4>::template mapping<std::extents<char, D>>>);
+  mixin_extents<hc, false, ac>(handle, layout_wrapping_integral<4>(), acc);
+}
+
+template <class T>
+constexpr void mixin_accessor() {
+  ElementPool<T, 1024> elements;
+  mixin_layout<true, true>(elements.get_ptr(), std::default_accessor<T>());
+
+  // Using weird accessor/data_handle
+  // Make sure they actually got the properties we want to test
+  // checked_accessor is not default constructible except for const double, where it is not noexcept
+  static_assert(std::is_default_constructible_v<checked_accessor<T>> == std::is_same_v<T, const double>);
+  // checked_accessor's data handle type is not default constructible for double
+  static_assert(
+      std::is_default_constructible_v<typename checked_accessor<T>::data_handle_type> != std::is_same_v<T, double>);
+  mixin_layout<!std::is_same_v<T, double>, std::is_same_v<T, const double>>(
+      typename checked_accessor<T>::data_handle_type(elements.get_ptr()), checked_accessor<T>(1024));
+}
+
+constexpr bool test() {
+  mixin_accessor<int>();
+  mixin_accessor<const int>();
+  mixin_accessor<double>();
+  mixin_accessor<const double>();
+  mixin_accessor<MinimalElementType>();
+  mixin_accessor<const MinimalElementType>();
+  return true;
+}
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}

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
new file mode 100644
index 00000000000000..e7fd81b3ab4e93
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_array.pass.cpp
@@ -0,0 +1,197 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, size_t N>
+//   constexpr explicit(N != rank_dynamic())
+//     mdspan(data_handle_type p, const array<OtherIndexType, N>& exts);
+//
+// Constraints:
+//   - is_convertible_v<const OtherIndexType&, index_type> is true,
+//   - (is_nothrow_constructible<index_type, const OtherIndexType&> && ...) is true,
+//   - N == rank() || N == rank_dynamic() is true,
+//   - is_constructible_v<mapping_type, extents_type> is true, and
+//   - is_default_constructible_v<accessor_type> is true.
+//
+// Preconditions: [0, map_.required_span_size()) is an accessible range of p and acc_
+//                for the values of map_ and acc_ after the invocation of this constructor.
+//
+// Effects:
+//   - Direct-non-list-initializes ptr_ with std::move(p),
+//   - direct-non-list-initializes map_ with extents_type(exts), and
+//   - value-initializes acc_.
+
+#include <array>
+#include <concepts>
+#include <cassert>
+#include <mdspan>
+#include <type_traits>
+
+#include "test_macros.h"
+
+#include "../ConvertibleToIntegral.h"
+#include "../MinimalElementType.h"
+#include "CustomTestLayouts.h"
+#include "CustomTestAccessors.h"
+
+template <class Extents, size_t... Idxs>
+constexpr auto array_from_extents(const Extents& exts, std::index_sequence<Idxs...>) {
+  return std::array<typename Extents::index_type, Extents::rank()>{exts.extent(Idxs)...};
+}
+
+template <class MDS, class Exts>
+concept check_mdspan_ctor_implicit = requires(MDS m, typename MDS::data_handle_type h, const Exts& exts) {
+  m = {h, exts};
+};
+
+template <class H, class M, class A, size_t N>
+constexpr void
+test_mdspan_ctor_array(const H& handle, const M& map, const A&, std::array<typename M::index_type, N> exts) {
+  using MDS = std::mdspan<typename A::element_type, typename M::extents_type, typename M::layout_type, A>;
+  if !consteval {
+    move_counted_handle<typename MDS::element_type>::move_counter() = 0;
+  }
+  MDS m(handle, exts);
+  if !consteval {
+    if constexpr (std::is_same_v<H, move_counted_handle<typename MDS::element_type>>) {
+      assert((H::move_counter() == 1));
+    }
+  }
+
+  static_assert(!noexcept(MDS(handle, exts)));
+
+  static_assert(check_mdspan_ctor_implicit<MDS, decltype(exts)> == (N == MDS::rank_dynamic()));
+
+  assert(m.extents() == map.extents());
+  if constexpr (std::equality_comparable<H>)
+    assert(m.data_handle() == handle);
+  if constexpr (std::equality_comparable<M>)
+    assert(m.mapping() == map);
+  if constexpr (std::equality_comparable<A>)
+    assert(m.accessor() == A());
+}
+
+template <bool mec, bool ac, class H, class M, class A>
+constexpr void test_mdspan_ctor(const H& handle, const M& map, const A& acc) {
+  using MDS = std::mdspan<typename A::element_type, typename M::extents_type, typename M::layout_type, A>;
+  static_assert(mec == std::is_constructible_v<M, typename M::extents_type>);
+  static_assert(ac == std::is_default_constructible_v<A>);
+  if constexpr (mec && ac) {
+    // test from all extents
+    auto exts = array_from_extents(map.extents(), std::make_index_sequence<MDS::rank()>());
+    test_mdspan_ctor_array(handle, map, acc, exts);
+
+    // test from dynamic extents
+    std::array<typename MDS::index_type, MDS::rank_dynamic()> exts_dynamic{};
+    size_t r_dyn = 0;
+    for (size_t r = 0; r < MDS::rank(); r++) {
+      if (MDS::static_extent(r) == std::dynamic_extent)
+        exts_dynamic[r_dyn++] = exts[r];
+    }
+    test_mdspan_ctor_array(handle, map, acc, exts_dynamic);
+  } else {
+    static_assert(!std::is_constructible_v<MDS, const H&, const std::array<typename MDS::index_type, MDS::rank()>&>);
+  }
+}
+
+template <bool mec, bool ac, class H, class L, class A>
+constexpr void mixin_extents(const H& handle, const L& layout, const A& acc) {
+  constexpr size_t D = std::dynamic_extent;
+  test_mdspan_ctor<mec, ac>(handle, construct_mapping(layout, std::extents<int>()), acc);
+  test_mdspan_ctor<mec, ac>(handle, construct_mapping(layout, std::extents<char, D>(7)), acc);
+  test_mdspan_ctor<mec, ac>(handle, construct_mapping(layout, std::extents<unsigned, 7>()), acc);
+  test_mdspan_ctor<mec, ac>(handle, construct_mapping(layout, std::extents<size_t, D, 4, D>(2, 3)), acc);
+  test_mdspan_ctor<mec, ac>(handle, construct_mapping(layout, std::extents<char, D, 7, D>(0, 3)), acc);
+  test_mdspan_ctor<mec, ac>(
+      handle, construct_mapping(layout, std::extents<int64_t, D, 7, D, 4, D, D>(1, 2, 3, 2)), acc);
+}
+
+template <bool ac, class H, class A>
+constexpr void mixin_layout(const H& handle, const A& acc) {
+  mixin_extents<true, ac>(handle, std::layout_left(), acc);
+  mixin_extents<true, ac>(handle, std::layout_right(), acc);
+
+  // Sanity check that this layouts mapping is constructible from extents (via its move constructor)
+  static_assert(std::is_constructible_v<typename layout_wrapping_integral<8>::template mapping<std::extents<int>>,
+                                        std::extents<int>>);
+  static_assert(!std::is_constructible_v<typename layout_wrapping_integral<8>::template mapping<std::extents<int>>,
+                                         const std::extents<int>&>);
+  mixin_extents<true, ac>(handle, layout_wrapping_integral<8>(), acc);
+  // Sanity check that this layouts mapping is not constructible from extents
+  static_assert(!std::is_constructible_v<typename layout_wrapping_integral<4>::template mapping<std::extents<int>>,
+                                         std::extents<int>>);
+  static_assert(!std::is_constructible_v<typename layout_wrapping_integral<4>::template mapping<std::extents<int>>,
+                                         const std::extents<int>&>);
+  mixin_extents<false, ac>(handle, layout_wrapping_integral<4>(), acc);
+}
+
+template <class T>
+constexpr void mixin_accessor() {
+  ElementPool<T, 1024> elements;
+  mixin_layout<true>(elements.get_ptr(), std::default_accessor<T>());
+
+  // Using weird accessor/data_handle
+  // Make sure they actually got the properties we want to test
+  // checked_accessor is not default constructible except for const double, where it is not noexcept
+  static_assert(std::is_default_constructible_v<checked_accessor<T>> == std::is_same_v<T, const double>);
+  mixin_layout<std::is_same_v<T, const double>>(
+      typename checked_accessor<T>::data_handle_type(elements.get_ptr()), checked_accessor<T>(1024));
+}
+
+constexpr bool test() {
+  mixin_accessor<int>();
+  mixin_accessor<const int>();
+  mixin_accessor<double>();
+  mixin_accessor<const double>();
+  mixin_accessor<MinimalElementType>();
+  mixin_accessor<const MinimalElementType>();
+
+  // test non-constructibility from wrong array type
+  constexpr size_t D = std::dynamic_extent;
+  using mds_t        = std::mdspan<float, std::extents<unsigned, 3, D, D>>;
+  // sanity check
+  static_assert(std::is_constructible_v<mds_t, float*, std::array<int, 3>>);
+  static_assert(std::is_constructible_v<mds_t, float*, std::array<int, 2>>);
+  // wrong size
+  static_assert(!std::is_constructible_v<mds_t, float*, std::array<int, 1>>);
+  static_assert(!std::is_constructible_v<mds_t, float*, std::array<int, 4>>);
+  // not convertible to index_type
+  static_assert(std::is_convertible_v<const IntType&, int>);
+  static_assert(!std::is_convertible_v<const IntType&, unsigned>);
+  static_assert(!std::is_constructible_v<mds_t, float*, std::array<IntType, 2>>);
+
+  // index_type is not nothrow constructible
+  using mds_uchar_t = std::mdspan<float, std::extents<unsigned char, 3, D, D>>;
+  static_assert(std::is_convertible_v<IntType, unsigned char>);
+  static_assert(std::is_convertible_v<const IntType&, unsigned char>);
+  static_assert(!std::is_nothrow_constructible_v<unsigned char, const IntType&>);
+  static_assert(!std::is_constructible_v<mds_uchar_t, float*, std::array<IntType, 2>>);
+
+  // convertible from non-const to index_type but not  from const
+  using mds_int_t = std::mdspan<float, std::extents<int, 3, D, D>>;
+  static_assert(std::is_convertible_v<IntTypeNC, int>);
+  static_assert(!std::is_convertible_v<const IntTypeNC&, int>);
+  static_assert(std::is_nothrow_constructible_v<int, IntTypeNC>);
+  static_assert(!std::is_constructible_v<mds_int_t, float*, std::array<IntTypeNC, 2>>);
+
+  // can't test a combo where std::is_nothrow_constructible_v<int, const IntTypeNC&> is true,
+  // but std::is_convertible_v<const IntType&, int> is false
+
+  // test non-constructibility from wrong handle_type
+  static_assert(!std::is_constructible_v<mds_t, const float*, std::array<int, 2>>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}

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
new file mode 100644
index 00000000000000..c194fdaeffd138
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_extents.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>
+
+// constexpr mdspan(data_handle_type p, const extents_type& ext);
+//
+// Constraints:
+//   - is_constructible_v<mapping_type, const extents_type&> is true, and
+//   - is_default_constructible_v<accessor_type> is true.
+//
+// Preconditions: [0, map_.required_span_size()) is an accessible range of p and acc_
+//                for the values of map_ and acc_ after the invocation of this constructor.
+//
+// Effects:
+//   - Direct-non-list-initializes ptr_ with std::move(p),
+//   - direct-non-list-initializes map_ with ext, and
+//   - value-initializes acc_.
+
+#include <mdspan>
+#include <type_traits>
+#include <concepts>
+#include <cassert>
+
+#include "test_macros.h"
+
+#include "../MinimalElementType.h"
+#include "CustomTestLayouts.h"
+#include "CustomTestAccessors.h"
+
+template <bool mec, bool ac, class H, class M, class A>
+constexpr void test_mdspan_types(const H& handle, const M& map, const A&) {
+  using MDS = std::mdspan<typename A::element_type, typename M::extents_type, typename M::layout_type, A>;
+
+  static_assert(mec == std::is_constructible_v<M, const typename M::extents_type&>);
+  static_assert(ac == std::is_default_constructible_v<A>);
+  if constexpr (mec && ac) {
+    if !consteval {
+      move_counted_handle<typename MDS::element_type>::move_counter() = 0;
+    }
+    // use formulation of constructor which tests that its not explicit
+    MDS m = {handle, map.extents()};
+    if !consteval {
+      if constexpr (std::is_same_v<H, move_counted_handle<typename MDS::element_type>>) {
+        assert((H::move_counter() == 1));
+      }
+    }
+    static_assert(!noexcept(MDS(handle, map.extents())));
+    assert(m.extents() == map.extents());
+    if constexpr (std::equality_comparable<H>)
+      assert(m.data_handle() == handle);
+    if constexpr (std::equality_comparable<M>)
+      assert(m.mapping() == map);
+    if constexpr (std::equality_comparable<A>)
+      assert(m.accessor() == A());
+  } else {
+    static_assert(!std::is_constructible_v<MDS, const H&, const typename M::extents_type&>);
+  }
+}
+
+template <bool mec, bool ac, class H, class L, class A>
+constexpr void mixin_extents(const H& handle, const L& layout, const A& acc) {
+  constexpr size_t D = std::dynamic_extent;
+  test_mdspan_types<mec, ac>(handle, construct_mapping(layout, std::extents<int>()), acc);
+  test_mdspan_types<mec, ac>(handle, construct_mapping(layout, std::extents<char, D>(7)), acc);
+  test_mdspan_types<mec, ac>(handle, construct_mapping(layout, std::extents<unsigned, 7>()), acc);
+  test_mdspan_types<mec, ac>(handle, construct_mapping(layout, std::extents<size_t, D, 4, D>(2, 3)), acc);
+  test_mdspan_types<mec, ac>(handle, construct_mapping(layout, std::extents<char, D, 7, D>(0, 3)), acc);
+  test_mdspan_types<mec, ac>(
+      handle, construct_mapping(layout, std::extents<int64_t, D, 7, D, 4, D, D>(1, 2, 3, 2)), acc);
+}
+
+template <bool ac, class H, class A>
+constexpr void mixin_layout(const H& handle, const A& acc) {
+  mixin_extents<true, ac>(handle, std::layout_left(), acc);
+  mixin_extents<true, ac>(handle, std::layout_right(), acc);
+
+  // Use weird layout, make sure it has the properties we want to test
+  // Sanity check that this layouts mapping is constructible from extents (via its move constructor)
+  static_assert(std::is_constructible_v<typename layout_wrapping_integral<8>::template mapping<std::extents<int>>,
+                                        std::extents<int>>);
+  static_assert(!std::is_constructible_v<typename layout_wrapping_integral<8>::template mapping<std::extents<int>>,
+                                         const std::extents<int>&>);
+  mixin_extents<false, ac>(handle, layout_wrapping_integral<8>(), acc);
+  // Sanity check that this layouts mapping is not constructible from extents
+  static_assert(!std::is_constructible_v<typename layout_wrapping_integral<4>::template mapping<std::extents<int>>,
+                                         std::extents<int>>);
+  static_assert(!std::is_constructible_v<typename layout_wrapping_integral<4>::template mapping<std::extents<int>>,
+                                         const std::extents<int>&>);
+  mixin_extents<false, ac>(handle, layout_wrapping_integral<4>(), acc);
+}
+
+template <class T>
+constexpr void mixin_accessor() {
+  ElementPool<T, 1024> elements;
+  mixin_layout<true>(elements.get_ptr(), std::default_accessor<T>());
+
+  // Using weird accessor/data_handle
+  // Make sure they actually got the properties we want to test
+  // checked_accessor is not default constructible except for const double, where it is not noexcept
+  static_assert(std::is_default_constructible_v<checked_accessor<T>> == std::is_same_v<T, const double>);
+  mixin_layout<std::is_same_v<T, const double>>(
+      typename checked_accessor<T>::data_handle_type(elements.get_ptr()), checked_accessor<T>(1024));
+}
+
+constexpr bool test() {
+  mixin_accessor<int>();
+  mixin_accessor<const int>();
+  mixin_accessor<double>();
+  mixin_accessor<const double>();
+  mixin_accessor<MinimalElementType>();
+  mixin_accessor<const MinimalElementType>();
+
+  // test non-constructibility from wrong extents type
+  constexpr size_t D = std::dynamic_extent;
+  using mds_t        = std::mdspan<float, std::extents<int, 3, D, D>>;
+  // sanity check
+  static_assert(std::is_constructible_v<mds_t, float*, std::extents<int, 3, D, D>>);
+  // wrong size
+  static_assert(!std::is_constructible_v<mds_t, float*, std::extents<int, D, D>>);
+  static_assert(!std::is_constructible_v<mds_t, float*, std::extents<int, D, D, D, D>>);
+  // wrong type in general: note the extents constructor does NOT convert, since it takes by const&
+  static_assert(!std::is_constructible_v<mds_t, float*, std::extents<int, D, D, D>>);
+  static_assert(!std::is_constructible_v<mds_t, float*, std::extents<unsigned, 3, D, D>>);
+
+  // test non-constructibility from wrong handle_type
+  static_assert(!std::is_constructible_v<mds_t, const float*, std::extents<int, 3, D, D>>);
+
+  return true;
+}
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}

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
new file mode 100644
index 00000000000000..1bd9e488ad0d41
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_integers.pass.cpp
@@ -0,0 +1,165 @@
+//===----------------------------------------------------------------------===//
+//
+// 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... OtherIndexTypes>
+//   constexpr explicit mdspan(data_handle_type p, OtherIndexTypes... exts);
+//
+// Let N be sizeof...(OtherIndexTypes).
+//
+// Constraints:
+//   - (is_convertible_v<OtherIndexTypes, index_type> && ...) is true,
+//   - (is_nothrow_constructible<index_type, OtherIndexTypes> && ...) is true,
+//   - N == rank() || N == rank_dynamic() is true,
+//   - is_constructible_v<mapping_type, extents_type> is true, and
+//   - is_default_constructible_v<accessor_type> is true.
+//
+// Preconditions: [0, map_.required_span_size()) is an accessible range of p and acc_
+//                for the values of map_ and acc_ after the invocation of this constructor.
+//
+// Effects:
+//   - Direct-non-list-initializes ptr_ with std::move(p),
+//   - direct-non-list-initializes map_ with extents_type(static_cast<index_type>(std::move(exts))...), and
+//   - value-initializes acc_.
+
+#include <array>
+#include <concepts>
+#include <cassert>
+#include <mdspan>
+#include <type_traits>
+
+#include "test_macros.h"
+
+#include "../MinimalElementType.h"
+#include "CustomTestLayouts.h"
+#include "CustomTestAccessors.h"
+
+template <class MDS, class... Args>
+concept check_mdspan_ctor_implicit = requires(MDS m, Args... args) { m = {args...}; };
+
+template <bool mec, bool ac, class H, class M, class A, class... Idxs>
+constexpr void test_mdspan_types(const H& handle, const M& map, const A&, Idxs... idxs) {
+  using MDS = std::mdspan<typename A::element_type, typename M::extents_type, typename M::layout_type, A>;
+
+  static_assert(mec == std::is_constructible_v<M, typename M::extents_type>);
+  static_assert(ac == std::is_default_constructible_v<A>);
+
+  if constexpr (mec && ac) {
+    if !consteval {
+      move_counted_handle<typename MDS::element_type>::move_counter() = 0;
+    }
+    MDS m(handle, idxs...);
+    if !consteval {
+      if constexpr (std::is_same_v<H, move_counted_handle<typename MDS::element_type>>) {
+        assert((H::move_counter() == 1));
+      }
+    }
+
+    // sanity check that concept works
+    static_assert(check_mdspan_ctor_implicit<MDS, H, std::array<typename MDS::index_type, MDS::rank_dynamic()>>);
+    // check that the constructor from integral is explicit
+    static_assert(!check_mdspan_ctor_implicit<MDS, H, Idxs...>);
+
+    assert(m.extents() == map.extents());
+    if constexpr (std::equality_comparable<H>)
+      assert(m.data_handle() == handle);
+    if constexpr (std::equality_comparable<M>)
+      assert(m.mapping() == map);
+    if constexpr (std::equality_comparable<A>)
+      assert(m.accessor() == A());
+  } else {
+    static_assert(!std::is_constructible_v<MDS, const H&, Idxs... >);
+  }
+}
+
+template <bool mec, bool ac, class H, class L, class A>
+constexpr void mixin_extents(const H& handle, const L& layout, const A& acc) {
+  constexpr size_t D = std::dynamic_extent;
+  // construct from just dynamic extents
+  test_mdspan_types<mec, ac>(handle, construct_mapping(layout, std::extents<int>()), acc);
+  test_mdspan_types<mec, ac>(handle, construct_mapping(layout, std::extents<char, D>(7)), acc, 7);
+  test_mdspan_types<mec, ac>(handle, construct_mapping(layout, std::extents<unsigned, 7>()), acc);
+  test_mdspan_types<mec, ac>(handle, construct_mapping(layout, std::extents<size_t, D, 4, D>(2, 3)), acc, 2, 3);
+  test_mdspan_types<mec, ac>(handle, construct_mapping(layout, std::extents<char, D, 7, D>(0, 3)), acc, 0, 3);
+  test_mdspan_types<mec, ac>(
+      handle, construct_mapping(layout, std::extents<int64_t, D, 7, D, 4, D, D>(1, 2, 3, 2)), acc, 1, 2, 3, 2);
+
+  // construct from all extents
+  test_mdspan_types<mec, ac>(handle, construct_mapping(layout, std::extents<unsigned, 7>()), acc, 7);
+  test_mdspan_types<mec, ac>(handle, construct_mapping(layout, std::extents<size_t, D, 4, D>(2, 3)), acc, 2, 4, 3);
+  test_mdspan_types<mec, ac>(handle, construct_mapping(layout, std::extents<char, D, 7, D>(0, 3)), acc, 0, 7, 3);
+  test_mdspan_types<mec, ac>(
+      handle, construct_mapping(layout, std::extents<int64_t, D, 7, D, 4, D, D>(1, 2, 3, 2)), acc, 1, 7, 2, 4, 3, 2);
+}
+
+template <bool ac, class H, class A>
+constexpr void mixin_layout(const H& handle, const A& acc) {
+  mixin_extents<true, ac>(handle, std::layout_left(), acc);
+  mixin_extents<true, ac>(handle, std::layout_right(), acc);
+
+  // Use weird layout, make sure it has the properties we want to test
+  // Sanity check that this layouts mapping is constructible from extents (via its move constructor)
+  static_assert(std::is_constructible_v<typename layout_wrapping_integral<8>::template mapping<std::extents<int>>,
+                                        std::extents<int>>);
+  static_assert(!std::is_constructible_v<typename layout_wrapping_integral<8>::template mapping<std::extents<int>>,
+                                         const std::extents<int>&>);
+  mixin_extents<true, ac>(handle, layout_wrapping_integral<8>(), acc);
+  // Sanity check that this layouts mapping is not constructible from extents
+  static_assert(!std::is_constructible_v<typename layout_wrapping_integral<4>::template mapping<std::extents<int>>,
+                                         std::extents<int>>);
+  static_assert(!std::is_constructible_v<typename layout_wrapping_integral<4>::template mapping<std::extents<int>>,
+                                         const std::extents<int>&>);
+  mixin_extents<false, ac>(handle, layout_wrapping_integral<4>(), acc);
+}
+
+template <class T>
+constexpr void mixin_accessor() {
+  ElementPool<T, 1024> elements;
+  mixin_layout<true>(elements.get_ptr(), std::default_accessor<T>());
+
+  // Using weird accessor/data_handle
+  // Make sure they actually got the properties we want to test
+  // checked_accessor is not default constructible except for const double, where it is not noexcept
+  static_assert(std::is_default_constructible_v<checked_accessor<T>> == std::is_same_v<T, const double>);
+  mixin_layout<std::is_same_v<T, const double>>(
+      typename checked_accessor<T>::data_handle_type(elements.get_ptr()), checked_accessor<T>(1024));
+}
+
+constexpr bool test() {
+  mixin_accessor<int>();
+  mixin_accessor<const int>();
+  mixin_accessor<double>();
+  mixin_accessor<const double>();
+  mixin_accessor<MinimalElementType>();
+  mixin_accessor<const MinimalElementType>();
+
+  // test non-constructibility from wrong integer types
+  constexpr size_t D = std::dynamic_extent;
+  using mds_t        = std::mdspan<float, std::extents<int, 3, D, D>>;
+  // sanity check
+  static_assert(std::is_constructible_v<mds_t, float*, int, int, int>);
+  static_assert(std::is_constructible_v<mds_t, float*, int, int>);
+  // wrong number of arguments
+  static_assert(!std::is_constructible_v<mds_t, float*, int>);
+  static_assert(!std::is_constructible_v<mds_t, float*, int, int, int, int>);
+  // not convertible to int
+  static_assert(!std::is_constructible_v<mds_t, float*, int, int, std::dextents<int, 1>>);
+
+  // test non-constructibility from wrong handle_type
+  static_assert(!std::is_constructible_v<mds_t, const float*, int, int>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}

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
new file mode 100644
index 00000000000000..9b1c6bd0668af9
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_map.pass.cpp
@@ -0,0 +1,129 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 mdspan(data_handle_type p, const mapping_type& m);
+//
+// Constraints: is_default_constructible_v<accessor_type> is true.
+//
+// Preconditions: [0, m.required_span_size()) is an accessible range of p and acc_
+//                for the value of acc_ after the invocation of this constructor.
+//
+// Effects:
+//   - Direct-non-list-initializes ptr_ with std::move(p),
+//   - direct-non-list-initializes map_ with m, and
+//   - value-initializes acc_.
+
+#include <mdspan>
+#include <type_traits>
+#include <concepts>
+#include <cassert>
+
+#include "test_macros.h"
+
+#include "../MinimalElementType.h"
+#include "CustomTestLayouts.h"
+#include "CustomTestAccessors.h"
+
+template <bool ac, class H, class M, class A>
+constexpr void test_mdspan_types(const H& handle, const M& map, const A&) {
+  using MDS = std::mdspan<typename A::element_type, typename M::extents_type, typename M::layout_type, A>;
+
+  static_assert(ac == std::is_default_constructible_v<A>);
+  if constexpr (ac) {
+    if !consteval {
+      move_counted_handle<typename MDS::element_type>::move_counter() = 0;
+    }
+    // use formulation of constructor which tests that it is not explicit
+    MDS m = {handle, map};
+    if !consteval {
+      if constexpr (std::is_same_v<H, move_counted_handle<typename MDS::element_type>>) {
+        assert((H::move_counter() == 1));
+      }
+    }
+    static_assert(!noexcept(MDS(handle, map)));
+    assert(m.extents() == map.extents());
+    if constexpr (std::equality_comparable<H>)
+      assert(m.data_handle() == handle);
+    if constexpr (std::equality_comparable<M>)
+      assert(m.mapping() == map);
+    if constexpr (std::equality_comparable<A>)
+      assert(m.accessor() == A());
+  } else {
+    static_assert(!std::is_constructible_v<MDS, const H&, const M&>);
+  }
+}
+
+template <bool ac, class H, class L, class A>
+constexpr void mixin_extents(const H& handle, const L& layout, const A& acc) {
+  constexpr size_t D = std::dynamic_extent;
+  test_mdspan_types<ac>(handle, construct_mapping(layout, std::extents<int>()), acc);
+  test_mdspan_types<ac>(handle, construct_mapping(layout, std::extents<char, D>(7)), acc);
+  test_mdspan_types<ac>(handle, construct_mapping(layout, std::extents<unsigned, 7>()), acc);
+  test_mdspan_types<ac>(handle, construct_mapping(layout, std::extents<size_t, D, 4, D>(2, 3)), acc);
+  test_mdspan_types<ac>(handle, construct_mapping(layout, std::extents<char, D, 7, D>(0, 3)), acc);
+  test_mdspan_types<ac>(handle, construct_mapping(layout, std::extents<int64_t, D, 7, D, 4, D, D>(1, 2, 3, 2)), acc);
+}
+
+template <bool ac, class H, class A>
+constexpr void mixin_layout(const H& handle, const A& acc) {
+  mixin_extents<ac>(handle, std::layout_left(), acc);
+  mixin_extents<ac>(handle, std::layout_right(), acc);
+  mixin_extents<ac>(handle, layout_wrapping_integral<4>(), acc);
+}
+
+template <class T>
+constexpr void mixin_accessor() {
+  ElementPool<T, 1024> elements;
+  mixin_layout<true>(elements.get_ptr(), std::default_accessor<T>());
+
+  // Using weird accessor/data_handle
+  // Make sure they actually got the properties we want to test
+  // checked_accessor is not default constructible except for const double, where it is not noexcept
+  static_assert(std::is_default_constructible_v<checked_accessor<T>> == std::is_same_v<T, const double>);
+  mixin_layout<std::is_same_v<T, const double>>(
+      typename checked_accessor<T>::data_handle_type(elements.get_ptr()), checked_accessor<T>(1024));
+}
+
+template <class E>
+using mapping_t = typename std::layout_right::template mapping<E>;
+
+constexpr bool test() {
+  mixin_accessor<int>();
+  mixin_accessor<const int>();
+  mixin_accessor<double>();
+  mixin_accessor<const double>();
+  mixin_accessor<MinimalElementType>();
+  mixin_accessor<const MinimalElementType>();
+
+  constexpr size_t D = std::dynamic_extent;
+  using mds_t        = std::mdspan<float, std::extents<int, 3, D, D>>;
+
+  // sanity check
+  static_assert(std::is_constructible_v<mds_t, float*, mapping_t<std::extents<int, 3, D, D>>>);
+
+  // test non-constructibility from wrong mapping type
+  // wrong rank
+  static_assert(!std::is_constructible_v<mds_t, float*, mapping_t<std::extents<int, D, D>>>);
+  static_assert(!std::is_constructible_v<mds_t, float*, mapping_t<std::extents<int, D, D, D, D>>>);
+  // wrong type in general: note the map constructor does NOT convert, since it takes by const&
+  static_assert(!std::is_constructible_v<mds_t, float*, mapping_t<std::extents<int, D, D, D>>>);
+  static_assert(!std::is_constructible_v<mds_t, float*, mapping_t<std::extents<unsigned, 3, D, D>>>);
+
+  // test non-constructibility from wrong handle_type
+  static_assert(!std::is_constructible_v<mds_t, const float*, mapping_t<std::extents<int, 3, D, D>>>);
+
+  return true;
+}
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}

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
new file mode 100644
index 00000000000000..7a94e8b0403269
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_map_acc.pass.cpp
@@ -0,0 +1,130 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 mdspan(data_handle_type p, const mapping_type& m, const accessor_type& a);
+//
+// Preconditions: [0, m.required_span_size()) is an accessible range of p and a.
+//
+// Effects:
+//   - Direct-non-list-initializes ptr_ with std::move(p),
+//   - direct-non-list-initializes map_ with m, and
+//   - direct-non-list-initializes acc_ with a.
+
+#include <mdspan>
+#include <type_traits>
+#include <concepts>
+#include <cassert>
+
+#include "test_macros.h"
+
+#include "../MinimalElementType.h"
+#include "CustomTestLayouts.h"
+#include "CustomTestAccessors.h"
+
+template <class H, class M, class A>
+constexpr void test_mdspan_types(const H& handle, const M& map, const A& acc) {
+  using MDS = std::mdspan<typename A::element_type, typename M::extents_type, typename M::layout_type, A>;
+
+  if !consteval {
+    move_counted_handle<typename MDS::element_type>::move_counter() = 0;
+  }
+  // use formulation of constructor which tests that it is not explicit
+  MDS m = {handle, map, acc};
+  if !consteval {
+    if constexpr (std::is_same_v<H, move_counted_handle<typename MDS::element_type>>) {
+      assert((H::move_counter() == 1));
+    }
+  }
+  static_assert(!noexcept(MDS(handle, map, acc)));
+  assert(m.extents() == map.extents());
+  if constexpr (std::equality_comparable<H>)
+    assert(m.data_handle() == handle);
+  if constexpr (std::equality_comparable<M>)
+    assert(m.mapping() == map);
+  if constexpr (std::equality_comparable<A>)
+    assert(m.accessor() == acc);
+}
+
+template <class H, class L, class A>
+constexpr void mixin_extents(const H& handle, const L& layout, const A& acc) {
+  constexpr size_t D = std::dynamic_extent;
+  test_mdspan_types(handle, construct_mapping(layout, std::extents<int>()), acc);
+  test_mdspan_types(handle, construct_mapping(layout, std::extents<char, D>(7)), acc);
+  test_mdspan_types(handle, construct_mapping(layout, std::extents<unsigned, 7>()), acc);
+  test_mdspan_types(handle, construct_mapping(layout, std::extents<size_t, D, 4, D>(2, 3)), acc);
+  test_mdspan_types(handle, construct_mapping(layout, std::extents<char, D, 7, D>(0, 3)), acc);
+  test_mdspan_types(handle, construct_mapping(layout, std::extents<int64_t, D, 7, D, 4, D, D>(1, 2, 3, 2)), acc);
+}
+
+template <class H, class A>
+constexpr void mixin_layout(const H& handle, const A& acc) {
+  mixin_extents(handle, std::layout_left(), acc);
+  mixin_extents(handle, std::layout_right(), acc);
+  mixin_extents(handle, layout_wrapping_integral<4>(), acc);
+}
+
+template <class T>
+constexpr void mixin_accessor() {
+  ElementPool<T, 1024> elements;
+  mixin_layout(elements.get_ptr(), std::default_accessor<T>());
+
+  // Using weird accessor/data_handle
+  // Make sure they actually got the properties we want to test
+  // checked_accessor is not default constructible except for const double, where it is not noexcept
+  static_assert(std::is_default_constructible_v<checked_accessor<T>> == std::is_same_v<T, const double>);
+  // checked_accessor's data handle type is not default constructible for double
+  static_assert(
+      std::is_default_constructible_v<typename checked_accessor<T>::data_handle_type> != std::is_same_v<T, double>);
+  mixin_layout(typename checked_accessor<T>::data_handle_type(elements.get_ptr()), checked_accessor<T>(1024));
+}
+
+template <class E>
+using mapping_t = typename std::layout_right::template mapping<E>;
+
+constexpr bool test() {
+  mixin_accessor<int>();
+  mixin_accessor<const int>();
+  mixin_accessor<double>();
+  mixin_accessor<const double>();
+  mixin_accessor<MinimalElementType>();
+  mixin_accessor<const MinimalElementType>();
+
+  // test non-constructibility from wrong args
+  constexpr size_t D = std::dynamic_extent;
+  using mds_t        = std::mdspan<float, std::extents<int, 3, D, D>>;
+  using acc_t        = std::default_accessor<float>;
+
+  // sanity check
+  static_assert(std::is_constructible_v<mds_t, float*, mapping_t<std::extents<int, 3, D, D>>, acc_t>);
+
+  // test non-constructibility from wrong accessor
+  static_assert(
+      !std::
+          is_constructible_v<mds_t, float*, mapping_t<std::extents<int, 3, D, D>>, std::default_accessor<const float>>);
+
+  // test non-constructibility from wrong mapping type
+  // wrong rank
+  static_assert(!std::is_constructible_v<mds_t, float*, mapping_t<std::extents<int, D, D>>, acc_t>);
+  static_assert(!std::is_constructible_v<mds_t, float*, mapping_t<std::extents<int, D, D, D, D>>, acc_t>);
+  // wrong type in general: note the map constructor does NOT convert, since it takes by const&
+  static_assert(!std::is_constructible_v<mds_t, float*, mapping_t<std::extents<int, D, D, D>>, acc_t>);
+  static_assert(!std::is_constructible_v<mds_t, float*, mapping_t<std::extents<unsigned, 3, D, D>>, acc_t>);
+
+  // test non-constructibility from wrong handle_type
+  static_assert(!std::is_constructible_v<mds_t, const float*, mapping_t<std::extents<int, 3, D, D>>, acc_t>);
+
+  return true;
+}
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}

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
new file mode 100644
index 00000000000000..f76c909783344f
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_span.pass.cpp
@@ -0,0 +1,197 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, size_t N>
+//   constexpr explicit(N != rank_dynamic())
+//     mdspan(data_handle_type p, span<OtherIndexType, N> exts);
+//
+// Constraints:
+//   - is_convertible_v<const OtherIndexType&, index_type> is true,
+//   - (is_nothrow_constructible<index_type, const OtherIndexType&> && ...) is true,
+//   - N == rank() || N == rank_dynamic() is true,
+//   - is_constructible_v<mapping_type, extents_type> is true, and
+//   - is_default_constructible_v<accessor_type> is true.
+//
+// Preconditions: [0, map_.required_span_size()) is an accessible range of p and acc_
+//                for the values of map_ and acc_ after the invocation of this constructor.
+//
+// Effects:
+//   - Direct-non-list-initializes ptr_ with std::move(p),
+//   - direct-non-list-initializes map_ with extents_type(exts), and
+//   - value-initializes acc_.
+
+#include <array>
+#include <concepts>
+#include <cassert>
+#include <mdspan>
+#include <type_traits>
+
+#include "test_macros.h"
+
+#include "../ConvertibleToIntegral.h"
+#include "../MinimalElementType.h"
+#include "CustomTestLayouts.h"
+#include "CustomTestAccessors.h"
+
+template <class Extents, size_t... Idxs>
+constexpr auto array_from_extents(const Extents& exts, std::index_sequence<Idxs...>) {
+  return std::array<typename Extents::index_type, Extents::rank()>{exts.extent(Idxs)...};
+}
+
+template <class MDS, class Exts>
+concept check_mdspan_ctor_implicit = requires(MDS m, typename MDS::data_handle_type h, const Exts& exts) {
+  m = {h, exts};
+};
+
+template <class H, class M, class A, size_t N>
+constexpr void
+test_mdspan_ctor_span(const H& handle, const M& map, const A&, std::span<typename M::index_type, N> exts) {
+  using MDS = std::mdspan<typename A::element_type, typename M::extents_type, typename M::layout_type, A>;
+  if !consteval {
+    move_counted_handle<typename MDS::element_type>::move_counter() = 0;
+  }
+  MDS m(handle, exts);
+  if !consteval {
+    if constexpr (std::is_same_v<H, move_counted_handle<typename MDS::element_type>>) {
+      assert((H::move_counter() == 1));
+    }
+  }
+
+  static_assert(!noexcept(MDS(handle, exts)));
+
+  static_assert(check_mdspan_ctor_implicit<MDS, decltype(exts)> == (N == MDS::rank_dynamic()));
+
+  assert(m.extents() == map.extents());
+  if constexpr (std::equality_comparable<H>)
+    assert(m.data_handle() == handle);
+  if constexpr (std::equality_comparable<M>)
+    assert(m.mapping() == map);
+  if constexpr (std::equality_comparable<A>)
+    assert(m.accessor() == A());
+}
+
+template <bool mec, bool ac, class H, class M, class A>
+constexpr void test_mdspan_ctor(const H& handle, const M& map, const A& acc) {
+  using MDS = std::mdspan<typename A::element_type, typename M::extents_type, typename M::layout_type, A>;
+  static_assert(mec == std::is_constructible_v<M, typename M::extents_type>);
+  static_assert(ac == std::is_default_constructible_v<A>);
+  if constexpr (mec && ac) {
+    // test from all extents
+    auto exts = array_from_extents(map.extents(), std::make_index_sequence<MDS::rank()>());
+    test_mdspan_ctor_span(handle, map, acc, std::span(exts));
+
+    // test from dynamic extents
+    std::array<typename MDS::index_type, MDS::rank_dynamic()> exts_dynamic{};
+    size_t r_dyn = 0;
+    for (size_t r = 0; r < MDS::rank(); r++) {
+      if (MDS::static_extent(r) == std::dynamic_extent)
+        exts_dynamic[r_dyn++] = exts[r];
+    }
+    test_mdspan_ctor_span(handle, map, acc, std::span(exts_dynamic));
+  } else {
+    static_assert(!std::is_constructible_v<MDS, const H&, std::span<typename MDS::index_type, MDS::rank()>>);
+  }
+}
+
+template <bool mec, bool ac, class H, class L, class A>
+constexpr void mixin_extents(const H& handle, const L& layout, const A& acc) {
+  constexpr size_t D = std::dynamic_extent;
+  test_mdspan_ctor<mec, ac>(handle, construct_mapping(layout, std::extents<int>()), acc);
+  test_mdspan_ctor<mec, ac>(handle, construct_mapping(layout, std::extents<char, D>(7)), acc);
+  test_mdspan_ctor<mec, ac>(handle, construct_mapping(layout, std::extents<unsigned, 7>()), acc);
+  test_mdspan_ctor<mec, ac>(handle, construct_mapping(layout, std::extents<size_t, D, 4, D>(2, 3)), acc);
+  test_mdspan_ctor<mec, ac>(handle, construct_mapping(layout, std::extents<char, D, 7, D>(0, 3)), acc);
+  test_mdspan_ctor<mec, ac>(
+      handle, construct_mapping(layout, std::extents<int64_t, D, 7, D, 4, D, D>(1, 2, 3, 2)), acc);
+}
+
+template <bool ac, class H, class A>
+constexpr void mixin_layout(const H& handle, const A& acc) {
+  mixin_extents<true, ac>(handle, std::layout_left(), acc);
+  mixin_extents<true, ac>(handle, std::layout_right(), acc);
+
+  // Sanity check that this layouts mapping is constructible from extents (via its move constructor)
+  static_assert(std::is_constructible_v<typename layout_wrapping_integral<8>::template mapping<std::extents<int>>,
+                                        std::extents<int>>);
+  static_assert(!std::is_constructible_v<typename layout_wrapping_integral<8>::template mapping<std::extents<int>>,
+                                         const std::extents<int>&>);
+  mixin_extents<true, ac>(handle, layout_wrapping_integral<8>(), acc);
+  // Sanity check that this layouts mapping is not constructible from extents
+  static_assert(!std::is_constructible_v<typename layout_wrapping_integral<4>::template mapping<std::extents<int>>,
+                                         std::extents<int>>);
+  static_assert(!std::is_constructible_v<typename layout_wrapping_integral<4>::template mapping<std::extents<int>>,
+                                         const std::extents<int>&>);
+  mixin_extents<false, ac>(handle, layout_wrapping_integral<4>(), acc);
+}
+
+template <class T>
+constexpr void mixin_accessor() {
+  ElementPool<T, 1024> elements;
+  mixin_layout<true>(elements.get_ptr(), std::default_accessor<T>());
+
+  // Using weird accessor/data_handle
+  // Make sure they actually got the properties we want to test
+  // checked_accessor is not default constructible except for const double, where it is not noexcept
+  static_assert(std::is_default_constructible_v<checked_accessor<T>> == std::is_same_v<T, const double>);
+  mixin_layout<std::is_same_v<T, const double>>(
+      typename checked_accessor<T>::data_handle_type(elements.get_ptr()), checked_accessor<T>(1024));
+}
+
+constexpr bool test() {
+  mixin_accessor<int>();
+  mixin_accessor<const int>();
+  mixin_accessor<double>();
+  mixin_accessor<const double>();
+  mixin_accessor<MinimalElementType>();
+  mixin_accessor<const MinimalElementType>();
+
+  // test non-constructibility from wrong span type
+  constexpr size_t D = std::dynamic_extent;
+  using mds_t        = std::mdspan<float, std::extents<unsigned, 3, D, D>>;
+  // sanity check
+  static_assert(std::is_constructible_v<mds_t, float*, std::span<int, 3>>);
+  static_assert(std::is_constructible_v<mds_t, float*, std::span<int, 2>>);
+  // wrong size
+  static_assert(!std::is_constructible_v<mds_t, float*, std::span<int, 1>>);
+  static_assert(!std::is_constructible_v<mds_t, float*, std::span<int, 4>>);
+  // not convertible to index_type
+  static_assert(std::is_convertible_v<const IntType&, int>);
+  static_assert(!std::is_convertible_v<const IntType&, unsigned>);
+  static_assert(!std::is_constructible_v<mds_t, float*, std::span<IntType, 2>>);
+
+  // index_type is not nothrow constructible
+  using mds_uchar_t = std::mdspan<float, std::extents<unsigned char, 3, D, D>>;
+  static_assert(std::is_convertible_v<IntType, unsigned char>);
+  static_assert(std::is_convertible_v<const IntType&, unsigned char>);
+  static_assert(!std::is_nothrow_constructible_v<unsigned char, const IntType&>);
+  static_assert(!std::is_constructible_v<mds_uchar_t, float*, std::span<IntType, 2>>);
+
+  // convertible from non-const to index_type but not  from const
+  using mds_int_t = std::mdspan<float, std::extents<int, 3, D, D>>;
+  static_assert(std::is_convertible_v<IntTypeNC, int>);
+  static_assert(!std::is_convertible_v<const IntTypeNC&, int>);
+  static_assert(std::is_nothrow_constructible_v<int, IntTypeNC>);
+  static_assert(!std::is_constructible_v<mds_int_t, float*, std::span<IntTypeNC, 2>>);
+
+  // can't test a combo where std::is_nothrow_constructible_v<int, const IntTypeNC&> is true,
+  // but std::is_convertible_v<const IntType&, int> is false
+
+  // test non-constructibility from wrong handle_type
+  static_assert(!std::is_constructible_v<mds_t, const float*, std::span<int, 2>>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}

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
new file mode 100644
index 00000000000000..aaaa4bc85709c2
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/ctor.move.pass.cpp
@@ -0,0 +1,98 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 mdspan(mdspan&&) = default;
+//
+// A specialization of mdspan is a trivially copyable type if its accessor_type, mapping_type, and data_handle_type are trivially copyable types.
+
+#include <mdspan>
+#include <type_traits>
+#include <concepts>
+#include <cassert>
+
+#include "test_macros.h"
+
+#include "../MinimalElementType.h"
+#include "CustomTestLayouts.h"
+#include "CustomTestAccessors.h"
+
+template <class H, class M, class A>
+constexpr void test_mdspan_types(const H& handle, const M& map, const A& acc) {
+  using MDS = std::mdspan<typename A::element_type, typename M::extents_type, typename M::layout_type, A>;
+
+  MDS m_org(handle, map, acc);
+  MDS m(std::move(m_org));
+  static_assert(std::is_trivially_move_constructible_v<MDS> ==
+                (std::is_trivially_move_constructible_v<H> && std::is_trivially_move_constructible_v<M> &&
+                 std::is_trivially_move_constructible_v<A>));
+  assert(m.extents() == map.extents());
+  if constexpr (std::equality_comparable<H>)
+    assert(m.data_handle() == handle);
+  if constexpr (std::equality_comparable<M>)
+    assert(m.mapping() == map);
+  if constexpr (std::equality_comparable<A>)
+    assert(m.accessor() == acc);
+}
+
+template <class H, class L, class A>
+constexpr void mixin_extents(const H& handle, const L& layout, const A& acc) {
+  constexpr size_t D = std::dynamic_extent;
+  test_mdspan_types(handle, construct_mapping(layout, std::extents<int>()), acc);
+  test_mdspan_types(handle, construct_mapping(layout, std::extents<char, D>(7)), acc);
+  test_mdspan_types(handle, construct_mapping(layout, std::extents<unsigned, 7>()), acc);
+  test_mdspan_types(handle, construct_mapping(layout, std::extents<size_t, D, 4, D>(2, 3)), acc);
+  test_mdspan_types(handle, construct_mapping(layout, std::extents<char, D, 7, D>(0, 3)), acc);
+  test_mdspan_types(handle, construct_mapping(layout, std::extents<int64_t, D, 7, D, 4, D, D>(1, 2, 3, 2)), acc);
+}
+
+template <class H, class A>
+constexpr void mixin_layout(const H& handle, const A& acc) {
+  // make sure we test a trivially copyable mapping
+  static_assert(std::is_trivially_move_constructible_v<typename std::layout_left::template mapping<std::extents<int>>>);
+  mixin_extents(handle, std::layout_left(), acc);
+  mixin_extents(handle, std::layout_right(), acc);
+  // make sure we test a not trivially copyable mapping
+  static_assert(!std::is_trivially_move_constructible_v<
+                typename layout_wrapping_integral<4>::template mapping<std::extents<int>>>);
+  mixin_extents(handle, layout_wrapping_integral<4>(), acc);
+}
+
+template <class T>
+constexpr void mixin_accessor() {
+  ElementPool<T, 1024> elements;
+  // make sure we test trivially constructible accessor and data_handle
+  static_assert(std::is_trivially_move_constructible_v<std::default_accessor<T>>);
+  static_assert(std::is_trivially_move_constructible_v<typename std::default_accessor<T>::data_handle_type>);
+  mixin_layout(elements.get_ptr(), std::default_accessor<T>());
+
+  // Using weird accessor/data_handle
+  // Make sure they actually got the properties we want to test
+  // checked_accessor is noexcept copy constructible except for const double
+  checked_accessor<T> acc(1024);
+  static_assert(std::is_trivially_move_constructible_v<typename checked_accessor<T>::data_handle_type> ==
+                std::is_same_v<T, double>);
+  mixin_layout(typename checked_accessor<T>::data_handle_type(elements.get_ptr()), acc);
+}
+
+constexpr bool test() {
+  mixin_accessor<int>();
+  mixin_accessor<const int>();
+  mixin_accessor<double>();
+  mixin_accessor<const double>();
+  mixin_accessor<MinimalElementType>();
+  mixin_accessor<const MinimalElementType>();
+  return true;
+}
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/views/mdspan/mdspan/deduction.pass.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/deduction.pass.cpp
new file mode 100644
index 00000000000000..69b2f7e39831af
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/deduction.pass.cpp
@@ -0,0 +1,159 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 CArray>
+//    requires(is_array_v<CArray> && rank_v<CArray> == 1)
+//    mdspan(CArray&)
+//      -> mdspan<remove_all_extents_t<CArray>, extents<size_t, extent_v<CArray, 0>>>;
+//
+//  template<class Pointer>
+//    requires(is_pointer_v<remove_reference_t<Pointer>>)
+//    mdspan(Pointer&&)
+//      -> mdspan<remove_pointer_t<remove_reference_t<Pointer>>, extents<size_t>>;
+//
+//  template<class ElementType, class... Integrals>
+//    requires((is_convertible_v<Integrals, size_t> && ...) && sizeof...(Integrals) > 0)
+//    explicit mdspan(ElementType*, Integrals...)
+//      -> mdspan<ElementType, dextents<size_t, sizeof...(Integrals)>>;
+//
+//  template<class ElementType, class OtherIndexType, size_t N>
+//    mdspan(ElementType*, span<OtherIndexType, N>)
+//      -> mdspan<ElementType, dextents<size_t, N>>;
+//
+//  template<class ElementType, class OtherIndexType, size_t N>
+//    mdspan(ElementType*, const array<OtherIndexType, N>&)
+//      -> mdspan<ElementType, dextents<size_t, N>>;
+//
+//  template<class ElementType, class IndexType, size_t... ExtentsPack>
+//    mdspan(ElementType*, const extents<IndexType, ExtentsPack...>&)
+//      -> mdspan<ElementType, extents<IndexType, ExtentsPack...>>;
+//
+//  template<class ElementType, class MappingType>
+//    mdspan(ElementType*, const MappingType&)
+//      -> mdspan<ElementType, typename MappingType::extents_type,
+//                typename MappingType::layout_type>;
+//
+//  template<class MappingType, class AccessorType>
+//    mdspan(const typename AccessorType::data_handle_type&, const MappingType&,
+//           const AccessorType&)
+//      -> mdspan<typename AccessorType::element_type, typename MappingType::extents_type,
+//                typename MappingType::layout_type, AccessorType>;
+
+#include <mdspan>
+#include <type_traits>
+#include <concepts>
+#include <cassert>
+
+#include "test_macros.h"
+
+#include "../MinimalElementType.h"
+#include "CustomTestLayouts.h"
+#include "CustomTestAccessors.h"
+
+template <class H, class M, class A>
+constexpr void test_mdspan_types(const H& handle, const M& map, const A& acc) {
+  using MDS = std::mdspan<typename A::element_type, typename M::extents_type, typename M::layout_type, A>;
+
+  // deduction from data_handle_type (including non-pointer), mapping and accessor
+  ASSERT_SAME_TYPE(decltype(std::mdspan(handle, map, acc)), MDS);
+
+  if constexpr (std::is_same_v<A, std::default_accessor<typename A::element_type>>) {
+    // deduction from pointer and mapping
+    // non-pointer data-handle-types have other accessor
+    ASSERT_SAME_TYPE(decltype(std::mdspan(handle, map)), MDS);
+    if constexpr (std::is_same_v<typename M::layout_type, std::layout_right>) {
+      // deduction from pointer and extents
+      ASSERT_SAME_TYPE(decltype(std::mdspan(handle, map.extents())), MDS);
+    }
+  }
+}
+
+template <class H, class L, class A>
+constexpr void mixin_extents(const H& handle, const L& layout, const A& acc) {
+  constexpr size_t D = std::dynamic_extent;
+  test_mdspan_types(handle, construct_mapping(layout, std::extents<int>()), acc);
+  test_mdspan_types(handle, construct_mapping(layout, std::extents<char, D>(7)), acc);
+  test_mdspan_types(handle, construct_mapping(layout, std::extents<unsigned, 7>()), acc);
+  test_mdspan_types(handle, construct_mapping(layout, std::extents<size_t, D, 4, D>(2, 3)), acc);
+  test_mdspan_types(handle, construct_mapping(layout, std::extents<char, D, 7, D>(0, 3)), acc);
+  test_mdspan_types(handle, construct_mapping(layout, std::extents<int64_t, D, 7, D, 4, D, D>(1, 2, 3, 2)), acc);
+}
+
+struct SizeTIntType {
+  size_t val;
+  constexpr SizeTIntType(size_t val_) : val(val_) {}
+  constexpr operator size_t() const noexcept { return size_t(val); }
+};
+
+template <class H, class A>
+  requires(sizeof(decltype(std::mdspan(std::declval<H>(), 10))) > 0)
+constexpr bool test_no_layout_deduction_guides(const H& handle, const A&) {
+  using T = typename A::element_type;
+  // deduction from pointer alone
+  ASSERT_SAME_TYPE(decltype(std::mdspan(handle)), std::mdspan<T, std::extents<size_t>>);
+  // deduction from pointer and integral like
+  ASSERT_SAME_TYPE(decltype(std::mdspan(handle, 5, SizeTIntType(6))), std::mdspan<T, std::dextents<size_t, 2>>);
+
+  std::array<char, 3> exts;
+  // deduction from pointer and array
+  ASSERT_SAME_TYPE(decltype(std::mdspan(handle, exts)), std::mdspan<T, std::dextents<size_t, 3>>);
+  // deduction from pointer and span
+  ASSERT_SAME_TYPE(decltype(std::mdspan(handle, std::span(exts))), std::mdspan<T, std::dextents<size_t, 3>>);
+  return true;
+}
+
+template <class H, class A>
+constexpr bool test_no_layout_deduction_guides(const H&, const A&) {
+  return false;
+}
+
+template <class H, class A>
+constexpr void mixin_layout(const H& handle, const A& acc) {
+  mixin_extents(handle, std::layout_left(), acc);
+  mixin_extents(handle, std::layout_right(), acc);
+  mixin_extents(handle, layout_wrapping_integral<4>(), acc);
+
+  // checking that there is no deduction happen for non-pointer handle type
+  assert((test_no_layout_deduction_guides(handle, acc) == std::is_same_v<H, typename A::element_type*>));
+}
+
+template <class T>
+constexpr void mixin_accessor() {
+  ElementPool<T, 1024> elements;
+  mixin_layout(elements.get_ptr(), std::default_accessor<T>());
+
+  // Using weird accessor/data_handle
+  // Make sure they actually got the properties we want to test
+  // checked_accessor is noexcept copy constructible except for const double
+  checked_accessor<T> acc(1024);
+  static_assert(noexcept(checked_accessor<T>(acc)) != std::is_same_v<T, const double>);
+  mixin_layout(typename checked_accessor<T>::data_handle_type(elements.get_ptr()), acc);
+}
+
+constexpr bool test() {
+  mixin_accessor<int>();
+  mixin_accessor<const int>();
+  mixin_accessor<double>();
+  mixin_accessor<const double>();
+  mixin_accessor<MinimalElementType>();
+  mixin_accessor<const MinimalElementType>();
+
+  // deduction from array alone
+  float a[12];
+  ASSERT_SAME_TYPE(decltype(std::mdspan(a)), std::mdspan<float, std::extents<size_t, 12>>);
+
+  return true;
+}
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/views/mdspan/mdspan/element_type.verify.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/element_type.verify.cpp
new file mode 100644
index 00000000000000..eca44cc7fe78f9
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/element_type.verify.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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <mdspan>
+
+// template<class ElementType, class Extents, class LayoutPolicy = layout_right, class AccessorPolicy = default_accessor>
+// class mdspan;
+//
+// Mandates:
+//   - ElementType is a complete object type that is neither an abstract class type nor an array type.
+//   - is_same_v<ElementType, typename AccessorPolicy::element_type> is true.
+
+#include <mdspan>
+
+class AbstractClass {
+public:
+  virtual void method() = 0;
+};
+
+void not_abstract_class() {
+  // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}mdspan: ElementType template parameter may not be an abstract class}}
+  [[maybe_unused]] std::mdspan<AbstractClass, std::extents<int>> m;
+}
+
+void not_array_type() {
+  // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}mdspan: ElementType template parameter may not be an array type}}
+  [[maybe_unused]] std::mdspan<int[5], std::extents<int>> m;
+}
+
+void element_type_mismatch() {
+  // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}mdspan: ElementType template parameter must match AccessorPolicy::element_type}}
+  [[maybe_unused]] std::mdspan<int, std::extents<int>, std::layout_right, std::default_accessor<const int>> m;
+}

diff  --git a/libcxx/test/std/containers/views/mdspan/mdspan/extents.verify.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/extents.verify.cpp
new file mode 100644
index 00000000000000..d6b6173c82b0e6
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/extents.verify.cpp
@@ -0,0 +1,23 @@
+//
+// 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 ElementType, class Extents, class LayoutPolicy = layout_right, class AccessorPolicy = default_accessor>
+// class mdspan;
+//
+// Mandates:
+//  - Extents is a specialization of extents
+
+#include <mdspan>
+
+void not_extents() {
+  // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}mdspan: Extents template parameter must be a specialization of extents.}}
+  [[maybe_unused]] std::mdspan<int, int> m;
+}

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
new file mode 100644
index 00000000000000..5284194632d0f4
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/index_operator.pass.cpp
@@ -0,0 +1,259 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 reference 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_.
+
+// GCC warns about comma operator changing its meaning inside [] in C++23
+#if defined(__GNUC__) && !defined(__clang_major__)
+#  pragma GCC diagnostic push
+#  pragma GCC diagnostic ignored "-Wcomma-subscript"
+#endif
+
+#include <mdspan>
+#include <cassert>
+#include <cstdint>
+
+#include "test_macros.h"
+
+#include "../ConvertibleToIntegral.h"
+#include "CustomTestLayouts.h"
+
+// Clang 15 and 16 do not support argument packs as input to operator []
+#if defined(__clang_major__) && __clang_major__ < 17
+template <class MDS>
+constexpr auto& access(MDS mds) {
+  return mds[];
+}
+template <class MDS>
+constexpr auto& access(MDS mds, int64_t i0) {
+  return mds[i0];
+}
+template <class MDS>
+constexpr auto& access(MDS mds, int64_t i0, int64_t i1) {
+  return mds[i0, i1];
+}
+template <class MDS>
+constexpr auto& access(MDS mds, int64_t i0, int64_t i1, int64_t i2) {
+  return mds[i0, i1, i2];
+}
+template <class MDS>
+constexpr auto& access(MDS mds, int64_t i0, int64_t i1, int64_t i2, int64_t i3) {
+  return mds[i0, i1, i2, i3];
+}
+#endif
+
+template <class MDS, class... Indices>
+concept operator_constraints = requires(MDS m, Indices... idxs) {
+  { std::is_same_v<decltype(m[idxs...]), typename MDS::reference> };
+};
+
+template <class MDS, class... Indices>
+  requires(operator_constraints<MDS, Indices...>)
+constexpr bool check_operator_constraints(MDS m, Indices... idxs) {
+  (void)m[idxs...];
+  return true;
+}
+
+template <class MDS, class... Indices>
+constexpr bool check_operator_constraints(MDS, Indices...) {
+  return false;
+}
+
+template <class MDS, class... Args>
+constexpr void iterate(MDS mds, Args... args) {
+  constexpr int r = static_cast<int>(MDS::extents_type::rank()) - 1 - static_cast<int>(sizeof...(Args));
+  if constexpr (-1 == r) {
+#if defined(__clang_major__) && __clang_major__ < 17
+    int* ptr1 = &access(mds, args...);
+#else
+    int* ptr1 = &mds[args...];
+#endif
+    int* ptr2 = &(mds.accessor().access(mds.data_handle(), mds.mapping()(args...)));
+    assert(ptr1 == ptr2);
+
+    std::array<typename MDS::index_type, MDS::rank()> args_arr{static_cast<typename MDS::index_type>(args)...};
+    int* ptr3 = &mds[args_arr];
+    assert(ptr3 == ptr2);
+    int* ptr4 = &mds[std::span(args_arr)];
+    assert(ptr4 == ptr2);
+  } else {
+    for (typename MDS::index_type i = 0; i < mds.extents().extent(r); i++) {
+      iterate(mds, i, args...);
+    }
+  }
+}
+
+template <class Mapping>
+constexpr void test_iteration(Mapping m) {
+  std::array<int, 1024> data;
+  using MDS = std::mdspan<int, typename Mapping::extents_type, typename Mapping::layout_type>;
+  MDS mds(data.data(), m);
+
+  iterate(mds);
+}
+
+template <class Layout>
+constexpr void test_layout() {
+  constexpr size_t D = std::dynamic_extent;
+  test_iteration(construct_mapping(Layout(), std::extents<int>()));
+  test_iteration(construct_mapping(Layout(), std::extents<unsigned, D>(1)));
+  test_iteration(construct_mapping(Layout(), std::extents<unsigned, D>(7)));
+  test_iteration(construct_mapping(Layout(), std::extents<unsigned, 7>()));
+  test_iteration(construct_mapping(Layout(), std::extents<unsigned, 7, 8>()));
+  test_iteration(construct_mapping(Layout(), std::extents<char, D, D, D, D>(1, 1, 1, 1)));
+
+// TODO enable for GCC 13, when the CI pipeline is switched, doesn't work with GCC 12
+#if defined(__clang_major__) && __clang_major__ >= 17
+  int data[1];
+  // Check operator constraint for number of arguments
+  static_assert(check_operator_constraints(std::mdspan(data, construct_mapping(Layout(), std::extents<int, D>(1))), 0));
+  static_assert(
+      !check_operator_constraints(std::mdspan(data, construct_mapping(Layout(), std::extents<int, D>(1))), 0, 0));
+
+  // Check operator constraint for convertibility of arguments to index_type
+  static_assert(
+      check_operator_constraints(std::mdspan(data, construct_mapping(Layout(), std::extents<int, D>(1))), IntType(0)));
+  static_assert(!check_operator_constraints(
+      std::mdspan(data, construct_mapping(Layout(), 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::mdspan(data, construct_mapping(Layout(), std::extents<unsigned char, D>(1))), IntType(0)));
+
+  // Check that mixed integrals work: note the second one tests that mdspan casts: layout_wrapping_integral does not accept IntType
+  static_assert(check_operator_constraints(
+      std::mdspan(data, construct_mapping(Layout(), std::extents<unsigned char, D, D>(1, 1))), int(0), size_t(0)));
+  static_assert(check_operator_constraints(
+      std::mdspan(data, construct_mapping(Layout(), std::extents<int, D, D>(1, 1))), unsigned(0), IntType(0)));
+
+  constexpr bool t = true;
+  constexpr bool o = false;
+  static_assert(!check_operator_constraints(
+      std::mdspan(data, construct_mapping(Layout(), std::extents<int, D, D>(1, 1))),
+      unsigned(0),
+      IntConfig<o, o, t, t>(0)));
+  static_assert(check_operator_constraints(
+      std::mdspan(data, construct_mapping(Layout(), std::extents<int, D, D>(1, 1))),
+      unsigned(0),
+      IntConfig<o, t, t, t>(0)));
+  static_assert(check_operator_constraints(
+      std::mdspan(data, construct_mapping(Layout(), std::extents<int, D, D>(1, 1))),
+      unsigned(0),
+      IntConfig<o, t, o, t>(0)));
+  static_assert(!check_operator_constraints(
+      std::mdspan(data, construct_mapping(Layout(), std::extents<int, D, D>(1, 1))),
+      unsigned(0),
+      IntConfig<t, o, o, t>(0)));
+  static_assert(check_operator_constraints(
+      std::mdspan(data, construct_mapping(Layout(), std::extents<int, D, D>(1, 1))),
+      unsigned(0),
+      IntConfig<t, o, t, o>(0)));
+
+  // layout_wrapped wouldn't quite work here the way we wrote the check
+  // IntConfig has configurable conversion properties: convert from const&, convert from non-const, no-throw-ctor from const&, no-throw-ctor from non-const
+  if constexpr (std::is_same_v<Layout, std::layout_left>) {
+    static_assert(!check_operator_constraints(
+        std::mdspan(data, construct_mapping(Layout(), std::extents<int, D>(1))), std::array{IntConfig<o, o, t, t>(0)}));
+    static_assert(!check_operator_constraints(
+        std::mdspan(data, construct_mapping(Layout(), std::extents<int, D>(1))), std::array{IntConfig<o, t, t, t>(0)}));
+    static_assert(!check_operator_constraints(
+        std::mdspan(data, construct_mapping(Layout(), std::extents<int, D>(1))), std::array{IntConfig<t, o, o, t>(0)}));
+    static_assert(!check_operator_constraints(
+        std::mdspan(data, construct_mapping(Layout(), std::extents<int, D>(1))), std::array{IntConfig<t, t, o, t>(0)}));
+    static_assert(check_operator_constraints(
+        std::mdspan(data, construct_mapping(Layout(), std::extents<int, D>(1))), std::array{IntConfig<t, o, t, o>(0)}));
+    static_assert(check_operator_constraints(
+        std::mdspan(data, construct_mapping(Layout(), std::extents<int, D>(1))), std::array{IntConfig<t, t, t, t>(0)}));
+
+    {
+      std::array idx{IntConfig<o, o, t, t>(0)};
+      std::span s(idx);
+      assert(!check_operator_constraints(std::mdspan(data, construct_mapping(Layout(), std::extents<int, D>(1))), s));
+    }
+    {
+      std::array idx{IntConfig<o, o, t, t>(0)};
+      std::span s(idx);
+      assert(!check_operator_constraints(std::mdspan(data, construct_mapping(Layout(), std::extents<int, D>(1))), s));
+    }
+    {
+      std::array idx{IntConfig<o, o, t, t>(0)};
+      std::span s(idx);
+      assert(!check_operator_constraints(std::mdspan(data, construct_mapping(Layout(), std::extents<int, D>(1))), s));
+    }
+    {
+      std::array idx{IntConfig<o, o, t, t>(0)};
+      std::span s(idx);
+      assert(!check_operator_constraints(std::mdspan(data, construct_mapping(Layout(), std::extents<int, D>(1))), s));
+    }
+    {
+      std::array idx{IntConfig<o, o, t, t>(0)};
+      std::span s(idx);
+      assert(!check_operator_constraints(std::mdspan(data, construct_mapping(Layout(), std::extents<int, D>(1))), s));
+    }
+    {
+      std::array idx{IntConfig<o, o, t, t>(0)};
+      std::span s(idx);
+      assert(!check_operator_constraints(std::mdspan(data, construct_mapping(Layout(), std::extents<int, D>(1))), s));
+    }
+  }
+#endif
+}
+
+template <class Layout>
+constexpr void test_layout_large() {
+  constexpr size_t D = std::dynamic_extent;
+  test_iteration(construct_mapping(Layout(), std::extents<int64_t, D, 4, D, D>(3, 5, 6)));
+  test_iteration(construct_mapping(Layout(), std::extents<int64_t, D, 4, 1, D>(3, 6)));
+}
+
+// mdspan::operator[] casts to index_type before calling mapping
+// mapping requirements only require the index operator to mixed integer types not anything convertible to index_type
+constexpr void test_index_cast_happens() {}
+
+constexpr bool test() {
+  test_layout<std::layout_left>();
+  test_layout<std::layout_right>();
+  test_layout<layout_wrapping_integral<4>>();
+  return true;
+}
+
+constexpr bool test_large() {
+  test_layout_large<std::layout_left>();
+  test_layout_large<std::layout_right>();
+  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;
+}
+#if defined(__GNUC__) && !defined(__clang_major__)
+#  pragma GCC diagnostic pop
+#endif

diff  --git a/libcxx/test/std/containers/views/mdspan/mdspan/mapping.verify.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/mapping.verify.cpp
new file mode 100644
index 00000000000000..51d8864ff9b72e
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/mapping.verify.cpp
@@ -0,0 +1,23 @@
+//
+// 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 ElementType, class Extents, class LayoutPolicy = layout_right, class AccessorPolicy = default_accessor>
+// class mdspan;
+//
+// Mandates:
+//  - LayoutPolicy shall meet the layout mapping policy requirements ([mdspan.layout.policy.reqmts])
+
+#include <mdspan>
+
+void not_layout_policy() {
+  // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}mdspan: LayoutPolicy template parameter is invalid. A common mistake is to pass a layout mapping instead of a layout policy}}
+  [[maybe_unused]] std::mdspan<int, std::extents<int>, std::layout_left::template mapping<std::extents<int>>> m;
+}

diff  --git a/libcxx/test/std/containers/views/mdspan/mdspan/move.pass.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/move.pass.cpp
new file mode 100644
index 00000000000000..544a5b8aa1ede4
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/move.pass.cpp
@@ -0,0 +1,100 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 mdspan& operator=(mdspan&&) = default;
+//
+// A specialization of mdspan is a trivially copyable type if its accessor_type, mapping_type, and data_handle_type are trivially copyable types.
+
+#include <mdspan>
+#include <type_traits>
+#include <concepts>
+#include <cassert>
+
+#include "test_macros.h"
+
+#include "../MinimalElementType.h"
+#include "CustomTestLayouts.h"
+#include "CustomTestAccessors.h"
+
+template <class H, class M, class A>
+constexpr void test_mdspan_types(const H& handle, const M& map, const A& acc) {
+  using MDS = std::mdspan<typename A::element_type, typename M::extents_type, typename M::layout_type, A>;
+
+  MDS m_org(handle, map, acc);
+  MDS m_copy(m_org);
+  MDS m(std::move(m_copy));
+
+  assert(m.extents() == map.extents());
+  if constexpr (std::equality_comparable<H>)
+    assert(m.data_handle() == handle);
+  if constexpr (std::equality_comparable<M>)
+    assert(m.mapping() == map);
+  if constexpr (std::equality_comparable<A>)
+    assert(m.accessor() == acc);
+
+  static_assert(std::is_trivially_move_assignable_v<MDS> ==
+                (std::is_trivially_move_assignable_v<H> && std::is_trivially_move_assignable_v<M> &&
+                 std::is_trivially_move_assignable_v<A>));
+}
+
+template <class H, class L, class A>
+constexpr void mixin_extents(const H& handle, const L& layout, const A& acc) {
+  constexpr size_t D = std::dynamic_extent;
+  test_mdspan_types(handle, construct_mapping(layout, std::extents<int>()), acc);
+  test_mdspan_types(handle, construct_mapping(layout, std::extents<char, D>(7)), acc);
+  test_mdspan_types(handle, construct_mapping(layout, std::extents<unsigned, 7>()), acc);
+  test_mdspan_types(handle, construct_mapping(layout, std::extents<size_t, D, 4, D>(2, 3)), acc);
+  test_mdspan_types(handle, construct_mapping(layout, std::extents<char, D, 7, D>(0, 3)), acc);
+  test_mdspan_types(handle, construct_mapping(layout, std::extents<int64_t, D, 7, D, 4, D, D>(1, 2, 3, 2)), acc);
+}
+
+template <class H, class A>
+constexpr void mixin_layout(const H& handle, const A& acc) {
+  // make sure we test a trivially copyable mapping
+  static_assert(std::is_trivially_move_assignable_v<typename std::layout_left::template mapping<std::extents<int>>>);
+  mixin_extents(handle, std::layout_left(), acc);
+  mixin_extents(handle, std::layout_right(), acc);
+  // make sure we test a not trivially copyable mapping
+  static_assert(
+      !std::is_trivially_move_assignable_v<typename layout_wrapping_integral<4>::template mapping<std::extents<int>>>);
+  mixin_extents(handle, layout_wrapping_integral<4>(), acc);
+}
+
+template <class T>
+constexpr void mixin_accessor() {
+  ElementPool<T, 1024> elements;
+  // make sure we test trivially constructible accessor and data_handle
+  static_assert(std::is_trivially_copyable_v<std::default_accessor<T>>);
+  static_assert(std::is_trivially_copyable_v<typename std::default_accessor<T>::data_handle_type>);
+  mixin_layout(elements.get_ptr(), std::default_accessor<T>());
+
+  // Using weird accessor/data_handle
+  // Make sure they actually got the properties we want to test
+  // checked_accessor is noexcept copy constructible except for const double
+  checked_accessor<T> acc(1024);
+  static_assert(noexcept(checked_accessor<T>(acc)) != std::is_same_v<T, const double>);
+  mixin_layout(typename checked_accessor<T>::data_handle_type(elements.get_ptr()), acc);
+}
+
+constexpr bool test() {
+  mixin_accessor<int>();
+  mixin_accessor<const int>();
+  mixin_accessor<double>();
+  mixin_accessor<const double>();
+  mixin_accessor<MinimalElementType>();
+  mixin_accessor<const MinimalElementType>();
+  return true;
+}
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/views/mdspan/mdspan/properties.pass.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/properties.pass.cpp
new file mode 100644
index 00000000000000..c822c3ad9c24f0
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/properties.pass.cpp
@@ -0,0 +1,214 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 ElementType, class Extents, class LayoutPolicy = layout_right,
+//            class AccessorPolicy = default_accessor<ElementType>>
+//   class mdspan {
+//   public:
+//     static constexpr rank_type rank() noexcept { return extents_type::rank(); }
+//     static constexpr rank_type rank_dynamic() noexcept { return extents_type::rank_dynamic(); }
+//     static constexpr size_t static_extent(rank_type r) noexcept
+//       { return extents_type::static_extent(r); }
+//     constexpr index_type extent(rank_type r) const noexcept { return extents().extent(r); }
+//
+//     constexpr size_type size() const noexcept;
+//     [[nodiscard]] constexpr bool empty() const noexcept;
+//
+//
+//     constexpr const extents_type& extents() const noexcept { return map_.extents(); }
+//     constexpr const data_handle_type& data_handle() const noexcept { return ptr_; }
+//     constexpr const mapping_type& mapping() const noexcept { return map_; }
+//     constexpr const accessor_type& accessor() const noexcept { return acc_; }
+//     static constexpr bool is_always_unique()
+//       { return mapping_type::is_always_unique(); }
+//     static constexpr bool is_always_exhaustive()
+//       { return mapping_type::is_always_exhaustive(); }
+//     static constexpr bool is_always_strided()
+//       { return mapping_type::is_always_strided(); }
+//
+//     constexpr bool is_unique() const
+//       { return map_.is_unique(); }
+//     constexpr bool is_exhaustive() const
+//       { return map_.is_exhaustive(); }
+//     constexpr bool is_strided() const
+//       { return map_.is_strided(); }
+//     constexpr index_type stride(rank_type r) const
+//       { return map_.stride(r); }
+//   };
+//
+// Each specialization MDS of mdspan models copyable and
+//    - is_nothrow_move_constructible_v<MDS> is true,
+//    - is_nothrow_move_assignable_v<MDS> is true, and
+//    - is_nothrow_swappable_v<MDS> is true.
+// A specialization of mdspan is a trivially copyable type if its accessor_type, mapping_type, and data_handle_type are trivially copyable types.
+
+#include <mdspan>
+#include <type_traits>
+#include <concepts>
+#include <cassert>
+
+#include "test_macros.h"
+
+#include "../MinimalElementType.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) {
+  using MDS = std::mdspan<typename A::element_type, typename M::extents_type, typename M::layout_type, A>;
+  MDS m(handle, map, acc);
+
+  // =====================================
+  // Traits for every mdspan
+  // =====================================
+  static_assert(std::copyable<MDS>);
+  static_assert(std::is_nothrow_move_constructible_v<MDS>);
+  static_assert(std::is_nothrow_move_assignable_v<MDS>);
+  static_assert(std::is_nothrow_swappable_v<MDS>);
+
+  // =====================================
+  // Invariants coming from data handle
+  // =====================================
+  // data_handle()
+  ASSERT_SAME_TYPE(decltype(m.data_handle()), const H&);
+  ASSERT_NOEXCEPT(m.data_handle());
+  if constexpr (std::equality_comparable<H>) {
+    assert(m.data_handle() == handle);
+  }
+
+  // =====================================
+  // Invariants coming from extents
+  // =====================================
+
+  // extents()
+  ASSERT_SAME_TYPE(decltype(m.extents()), const typename MDS::extents_type&);
+  ASSERT_NOEXCEPT(m.extents());
+  assert(m.extents() == map.extents());
+
+  // rank()
+  ASSERT_SAME_TYPE(decltype(m.rank()), typename MDS::rank_type);
+  ASSERT_NOEXCEPT(m.rank());
+  assert(MDS::rank() == MDS::extents_type::rank());
+
+  // rank_dynamic()
+  ASSERT_SAME_TYPE(decltype(m.rank_dynamic()), typename MDS::rank_type);
+  ASSERT_NOEXCEPT(m.rank_dynamic());
+  assert(MDS::rank_dynamic() == MDS::extents_type::rank_dynamic());
+
+  // extent(r), static_extent(r), size()
+  if constexpr (MDS::rank() > 0) {
+    typename MDS::size_type size = 1;
+    for (typename MDS::rank_type r = 0; r < MDS::rank(); r++) {
+      ASSERT_SAME_TYPE(decltype(MDS::static_extent(r)), size_t);
+      ASSERT_NOEXCEPT(MDS::static_extent(r));
+      assert(MDS::static_extent(r) == MDS::extents_type::static_extent(r));
+      ASSERT_SAME_TYPE(decltype(m.extent(r)), typename MDS::index_type);
+      ASSERT_NOEXCEPT(m.extent(r));
+      assert(m.extent(r) == m.extents().extent(r));
+      size *= m.extent(r);
+    }
+    assert(m.size() == size);
+  } else {
+    assert(m.size() == 1);
+  }
+  ASSERT_SAME_TYPE(decltype(m.size()), typename MDS::size_type);
+  ASSERT_NOEXCEPT(m.size());
+
+  // empty()
+  ASSERT_SAME_TYPE(decltype(m.empty()), bool);
+  ASSERT_NOEXCEPT(m.empty());
+  assert(m.empty() == (m.size() == 0));
+
+  // =====================================
+  // Invariants coming from mapping
+  // =====================================
+
+  // mapping()
+  ASSERT_SAME_TYPE(decltype(m.mapping()), const M&);
+  ASSERT_NOEXCEPT(m.mapping());
+
+  // is_[always_]unique/exhaustive/strided()
+  ASSERT_SAME_TYPE(decltype(MDS::is_always_unique()), bool);
+  ASSERT_SAME_TYPE(decltype(MDS::is_always_exhaustive()), bool);
+  ASSERT_SAME_TYPE(decltype(MDS::is_always_strided()), bool);
+  ASSERT_SAME_TYPE(decltype(m.is_unique()), bool);
+  ASSERT_SAME_TYPE(decltype(m.is_exhaustive()), bool);
+  ASSERT_SAME_TYPE(decltype(m.is_strided()), bool);
+  assert(!noexcept(MDS::is_always_unique()));
+  assert(!noexcept(MDS::is_always_exhaustive()));
+  assert(!noexcept(MDS::is_always_strided()));
+  assert(!noexcept(m.is_unique()));
+  assert(!noexcept(m.is_exhaustive()));
+  assert(!noexcept(m.is_strided()));
+  assert(MDS::is_always_unique() == M::is_always_unique());
+  assert(MDS::is_always_exhaustive() == M::is_always_exhaustive());
+  assert(MDS::is_always_strided() == M::is_always_strided());
+  assert(m.is_unique() == map.is_unique());
+  assert(m.is_exhaustive() == map.is_exhaustive());
+  assert(m.is_strided() == map.is_strided());
+
+  // stride(r)
+  if constexpr (MDS::rank() > 0) {
+    if (m.is_strided()) {
+      for (typename MDS::rank_type r = 0; r < MDS::rank(); r++) {
+        ASSERT_SAME_TYPE(decltype(m.stride(r)), typename MDS::index_type);
+        assert(!noexcept(m.stride(r)));
+        assert(m.stride(r) == map.stride(r));
+      }
+    }
+  }
+
+  // =====================================
+  // Invariants coming from accessor
+  // =====================================
+
+  // accessor()
+  ASSERT_SAME_TYPE(decltype(m.accessor()), const A&);
+  ASSERT_NOEXCEPT(m.accessor());
+}
+
+template <class H, class L, class A>
+constexpr void mixin_extents(const H& handle, const L& layout, const A& acc) {
+  constexpr size_t D = std::dynamic_extent;
+  test_mdspan_types(handle, construct_mapping(layout, std::extents<int>()), acc);
+  test_mdspan_types(handle, construct_mapping(layout, std::extents<char, D>(7)), acc);
+  test_mdspan_types(handle, construct_mapping(layout, std::extents<unsigned, 7>()), acc);
+  test_mdspan_types(handle, construct_mapping(layout, std::extents<size_t, D, 4, D>(2, 3)), acc);
+  test_mdspan_types(handle, construct_mapping(layout, std::extents<char, D, 7, D>(0, 3)), acc);
+  test_mdspan_types(handle, construct_mapping(layout, std::extents<int64_t, D, 7, D, 4, D, D>(1, 2, 3, 2)), acc);
+}
+
+template <class H, class A>
+constexpr void mixin_layout(const H& handle, const A& acc) {
+  mixin_extents(handle, std::layout_left(), acc);
+  mixin_extents(handle, std::layout_right(), acc);
+  mixin_extents(handle, layout_wrapping_integral<4>(), acc);
+}
+
+template <class T>
+constexpr void mixin_accessor() {
+  ElementPool<T, 1024> elements;
+  mixin_layout(elements.get_ptr(), std::default_accessor<T>());
+}
+
+constexpr bool test() {
+  mixin_accessor<int>();
+  mixin_accessor<const int>();
+  mixin_accessor<double>();
+  mixin_accessor<const double>();
+  mixin_accessor<MinimalElementType>();
+  mixin_accessor<const MinimalElementType>();
+  return true;
+}
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/views/mdspan/mdspan/swap.pass.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/swap.pass.cpp
new file mode 100644
index 00000000000000..dd1336c7af07e8
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/swap.pass.cpp
@@ -0,0 +1,76 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+//
+// friend constexpr void swap(mdspan& x, mdspan& y) noexcept;
+//
+// Effects: Equivalent to:
+//   swap(x.ptr_, y.ptr_);
+//   swap(x.map_, y.map_);
+//   swap(x.acc_, y.acc_);
+
+#include <mdspan>
+#include <type_traits>
+#include <concepts>
+#include <cassert>
+
+#include "test_macros.h"
+
+#include "../MinimalElementType.h"
+#include "CustomTestLayouts.h"
+
+template <class MDS>
+constexpr void test_swap(MDS a, MDS b) {
+  auto org_a = a;
+  auto org_b = b;
+  swap(a, b);
+  assert(a.extents() == org_b.extents());
+  assert(b.extents() == org_a.extents());
+  if constexpr (std::equality_comparable<typename MDS::mapping_type>) {
+    assert(a.mapping() == org_b.mapping());
+    assert(b.mapping() == org_a.mapping());
+  }
+  if constexpr (std::equality_comparable<typename MDS::data_handle_type>) {
+    assert(a.data_handle() == org_b.data_handle());
+    assert(b.data_handle() == org_a.data_handle());
+  }
+  // This check uses a side effect of layout_wrapping_integral::swap to make sure
+  // mdspan calls the underlying components' swap via ADL
+  if !consteval {
+    if constexpr (std::is_same_v<typename MDS::layout_type, layout_wrapping_integral<4>>) {
+      assert(MDS::mapping_type::swap_counter() > 0);
+    }
+  }
+}
+
+constexpr bool test() {
+  using extents_t = std::extents<int, 4, std::dynamic_extent>;
+  float data_a[1024];
+  float data_b[1024];
+  {
+    std::mdspan a(data_a, extents_t(12));
+    std::mdspan b(data_b, extents_t(5));
+    test_swap(a, b);
+  }
+  {
+    layout_wrapping_integral<4>::template mapping<extents_t> map_a(extents_t(12), not_extents_constructible_tag()),
+        map_b(extents_t(5), not_extents_constructible_tag());
+    std::mdspan a(data_a, map_a);
+    std::mdspan b(data_b, map_b);
+    test_swap(a, b);
+  }
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/views/mdspan/mdspan/types.pass.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/types.pass.cpp
new file mode 100644
index 00000000000000..5effea554c0d22
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/types.pass.cpp
@@ -0,0 +1,180 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 ElementType, class Extents, class LayoutPolicy = layout_right,
+//           class AccessorPolicy = default_accessor<ElementType>>
+//  class mdspan {
+//  public:
+//    using extents_type = Extents;
+//    using layout_type = LayoutPolicy;
+//    using accessor_type = AccessorPolicy;
+//    using mapping_type = typename layout_type::template mapping<extents_type>;
+//    using element_type = ElementType;
+//    using value_type = remove_cv_t<element_type>;
+//    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 data_handle_type = typename accessor_type::data_handle_type;
+//    using reference = typename accessor_type::reference;
+//    ...
+//  };
+
+#include <mdspan>
+#include <type_traits>
+#include <concepts>
+#include <cassert>
+
+#include "test_macros.h"
+
+#include "../MinimalElementType.h"
+#include "CustomTestAccessors.h"
+#include "CustomTestLayouts.h"
+
+// Calculated expected size of an mdspan
+// Note this expectes that only default_accessor is empty
+template<class MDS>
+constexpr size_t expected_size() {
+  size_t sizeof_dht = sizeof(typename MDS::data_handle_type);
+  size_t result = sizeof_dht;
+  if(MDS::rank_dynamic() > 0) {
+    size_t alignof_idx = alignof(typename MDS::index_type);
+    size_t sizeof_idx = sizeof(typename MDS::index_type);
+    // add alignment if necessary
+    result += sizeof_dht%alignof_idx == 0?0:alignof_idx - (sizeof_dht%alignof_idx);
+    // add sizeof stored extents
+    result += MDS::rank_dynamic() * sizeof_idx;
+  }
+  using A = typename MDS::accessor_type;
+  if(!std::is_same_v<A, std::default_accessor<typename MDS::element_type>>) {
+    size_t alignof_acc = alignof(A);
+    size_t sizeof_acc = sizeof(A);
+    // add alignment if necessary
+    result += result%alignof_acc == 0?0:alignof_acc - (result%alignof_acc);
+    // add sizeof stored accessor
+    result += sizeof_acc;
+  }
+  // add alignment of the mdspan itself
+  result += result%alignof(MDS) == 0?0:alignof(MDS) - (result%alignof(MDS));
+  return result;
+}
+
+// check triviality
+template <class T>
+constexpr bool trv_df_ctor = std::is_trivially_default_constructible_v<T>;
+template <class T>
+constexpr bool trv_cp_ctor = std::is_trivially_copy_constructible_v<T>;
+template <class T>
+constexpr bool trv_mv_ctor = std::is_trivially_move_constructible_v<T>;
+template <class T>
+constexpr bool trv_dstruct = std::is_trivially_destructible_v<T>;
+template <class T>
+constexpr bool trv_cp_asgn = std::is_trivially_copy_assignable_v<T>;
+template <class T>
+constexpr bool trv_mv_asgn = std::is_trivially_move_assignable_v<T>;
+
+template <class MDS, bool default_ctor, bool copy_ctor, bool move_ctor, bool destr, bool copy_assign, bool move_assign>
+void check_triviality() {
+  static_assert(trv_df_ctor<MDS> == default_ctor);
+  static_assert(trv_cp_ctor<MDS> == copy_ctor);
+  static_assert(trv_mv_ctor<MDS> == move_ctor);
+  static_assert(trv_dstruct<MDS> == destr);
+  static_assert(trv_cp_asgn<MDS> == copy_assign);
+  static_assert(trv_mv_asgn<MDS> == move_assign);
+}
+
+template <class T, class E, class L, class A>
+void test_mdspan_types() {
+  using MDS = std::mdspan<T, E, L, A>;
+
+  ASSERT_SAME_TYPE(typename MDS::extents_type, E);
+  ASSERT_SAME_TYPE(typename MDS::layout_type, L);
+  ASSERT_SAME_TYPE(typename MDS::accessor_type, A);
+  ASSERT_SAME_TYPE(typename MDS::mapping_type, typename L::template mapping<E>);
+  ASSERT_SAME_TYPE(typename MDS::element_type, T);
+  ASSERT_SAME_TYPE(typename MDS::value_type, std::remove_cv_t<T>);
+  ASSERT_SAME_TYPE(typename MDS::index_type, typename E::index_type);
+  ASSERT_SAME_TYPE(typename MDS::size_type, typename E::size_type);
+  ASSERT_SAME_TYPE(typename MDS::rank_type, typename E::rank_type);
+  ASSERT_SAME_TYPE(typename MDS::data_handle_type, typename A::data_handle_type);
+  ASSERT_SAME_TYPE(typename MDS::reference, typename A::reference);
+
+// This miserably failed with clang-cl - likely because it doesn't honor/enable
+// no-unique-address fully by default
+#ifndef _WIN32
+  // check the size of mdspan
+  if constexpr (std::is_same_v<L, std::layout_left> || std::is_same_v<L, std::layout_right>) {
+    LIBCPP_STATIC_ASSERT(sizeof(MDS) == expected_size<MDS>());
+  }
+#endif
+
+  // check default template parameters:
+  ASSERT_SAME_TYPE(std::mdspan<T, E>, std::mdspan<T, E, std::layout_right, std::default_accessor<T>>);
+  ASSERT_SAME_TYPE(std::mdspan<T, E, L>, std::mdspan<T, E, L, std::default_accessor<T>>);
+
+  // check triviality
+  using DH = typename MDS::data_handle_type;
+  using MP = typename MDS::mapping_type;
+
+  check_triviality<MDS,
+                   false, // mdspan is never trivially constructible right now
+                   trv_cp_ctor<DH> && trv_cp_ctor<MP> && trv_cp_ctor<A>,
+                   trv_mv_ctor<DH> && trv_mv_ctor<MP> && trv_mv_ctor<A>,
+                   trv_dstruct<DH> && trv_dstruct<MP> && trv_dstruct<A>,
+                   trv_cp_asgn<DH> && trv_cp_asgn<MP> && trv_cp_asgn<A>,
+                   trv_mv_asgn<DH> && trv_mv_asgn<MP> && trv_mv_asgn<A>>();
+}
+
+template <class T, class L, class A>
+void mixin_extents() {
+  constexpr size_t D = std::dynamic_extent;
+  test_mdspan_types<T, std::extents<int>, L, A>();
+  test_mdspan_types<T, std::extents<char, D>, L, A>();
+  test_mdspan_types<T, std::dextents<char, 7>, L, A>();
+  test_mdspan_types<T, std::dextents<char, 9>, L, A>();
+  test_mdspan_types<T, std::extents<unsigned, 7>, L, A>();
+  test_mdspan_types<T, std::extents<unsigned, D, D, D>, L, A>();
+  test_mdspan_types<T, std::extents<size_t, D, 7, D>, L, A>();
+  test_mdspan_types<T, std::extents<int64_t, D, 7, D, 4, D, D>, L, A>();
+}
+
+template <class T, class A>
+void mixin_layout() {
+  mixin_extents<T, std::layout_left, A>();
+  mixin_extents<T, std::layout_right, A>();
+  mixin_extents<T, layout_wrapping_integral<4>, A>();
+}
+
+template <class T>
+void mixin_accessor() {
+  mixin_layout<T, std::default_accessor<T>>();
+  mixin_layout<T, checked_accessor<T>>();
+}
+
+int main(int, char**) {
+  mixin_accessor<int>();
+  mixin_accessor<const int>();
+  mixin_accessor<double>();
+  mixin_accessor<const double>();
+  mixin_accessor<MinimalElementType>();
+  mixin_accessor<const MinimalElementType>();
+
+  // sanity checks for triviality
+  check_triviality<std::mdspan<int, std::extents<int>>, false, true, true, true, true, true>();
+  check_triviality<std::mdspan<int, std::dextents<int, 1>>, false, true, true, true, true, true>();
+  check_triviality<std::mdspan<int, std::dextents<int, 1>, std::layout_right, checked_accessor<int>>,
+                   false,
+                   true,
+                   false,
+                   true,
+                   true,
+                   true>();
+  return 0;
+}


        


More information about the llvm-branch-commits mailing list