[libcxx-commits] [libcxx] fcaccf8 - [libcxx] Add mdspan/extents

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Tue May 16 14:31:20 PDT 2023


Author: Christian Trott
Date: 2023-05-16T14:30:36-07:00
New Revision: fcaccf817d31d39096f7d0e7014cd6fe2fa3a683

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

LOG: [libcxx] Add mdspan/extents

This patch adds std::extents. extents is one of the core classes used by std::mdspan. It describes a multi-dimensional index space with a mix of compile time and runtime sizes. Furthermore, it is templated on the index type used to describe the multi-dimensional index space.

The class is designed to be highly optimizable in performance critical code sections, and is fully useable in constant expressions contexts.

Testing of this class tends to be somewhat combinatorical, due to the large number of possible corner cases involved in situations where we have both runtime and compile time extents. To add to this, the class is designed to be interoperable (in particular constructible) from arguments which only need to be convertible to the index_type, but are otherwise arbitrary user types. For a larger discussion on the design of this class refer to: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p0009r18.html

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

Reviewed By: ldionne, #libc

Spies: libcxx-commits, H-G-Hristov, tschuett, philnik, arichardson, Mordante, crtrott

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

Added: 
    libcxx/include/__mdspan/extents.h
    libcxx/include/mdspan
    libcxx/test/std/containers/views/mdspan/extents/ConvertibleToIntegral.h
    libcxx/test/std/containers/views/mdspan/extents/CtorTestCombinations.h
    libcxx/test/std/containers/views/mdspan/extents/assert.conversion.pass.cpp
    libcxx/test/std/containers/views/mdspan/extents/assert.ctor_from_array.pass.cpp
    libcxx/test/std/containers/views/mdspan/extents/assert.ctor_from_integral.pass.cpp
    libcxx/test/std/containers/views/mdspan/extents/assert.ctor_from_span.pass.cpp
    libcxx/test/std/containers/views/mdspan/extents/assert.obs.pass.cpp
    libcxx/test/std/containers/views/mdspan/extents/comparison.pass.cpp
    libcxx/test/std/containers/views/mdspan/extents/conversion.pass.cpp
    libcxx/test/std/containers/views/mdspan/extents/ctad.pass.cpp
    libcxx/test/std/containers/views/mdspan/extents/ctor_default.pass.cpp
    libcxx/test/std/containers/views/mdspan/extents/ctor_from_array.pass.cpp
    libcxx/test/std/containers/views/mdspan/extents/ctor_from_integral.pass.cpp
    libcxx/test/std/containers/views/mdspan/extents/ctor_from_span.pass.cpp
    libcxx/test/std/containers/views/mdspan/extents/dextents.pass.cpp
    libcxx/test/std/containers/views/mdspan/extents/obs_static.pass.cpp
    libcxx/test/std/containers/views/mdspan/extents/types.pass.cpp
    libcxx/test/std/language.support/support.limits/support.limits.general/mdspan.version.compile.pass.cpp

Modified: 
    libcxx/CREDITS.TXT
    libcxx/docs/FeatureTestMacroTable.rst
    libcxx/docs/Status/Cxx2bPapers.csv
    libcxx/include/CMakeLists.txt
    libcxx/include/libcxx.imp
    libcxx/include/module.modulemap.in
    libcxx/include/version
    libcxx/test/libcxx/assertions/headers_declare_verbose_abort.sh.cpp
    libcxx/test/libcxx/clang_tidy.sh.cpp
    libcxx/test/libcxx/double_include.sh.cpp
    libcxx/test/libcxx/min_max_macros.compile.pass.cpp
    libcxx/test/libcxx/modules_include.sh.cpp
    libcxx/test/libcxx/nasty_macros.compile.pass.cpp
    libcxx/test/libcxx/no_assert_include.compile.pass.cpp
    libcxx/test/libcxx/private_headers.verify.cpp
    libcxx/test/libcxx/transitive_includes.sh.cpp
    libcxx/test/libcxx/transitive_includes/cxx03.csv
    libcxx/test/libcxx/transitive_includes/cxx11.csv
    libcxx/test/libcxx/transitive_includes/cxx14.csv
    libcxx/test/libcxx/transitive_includes/cxx17.csv
    libcxx/test/libcxx/transitive_includes/cxx20.csv
    libcxx/test/libcxx/transitive_includes/cxx2b.csv
    libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
    libcxx/utils/generate_feature_test_macro_components.py

Removed: 
    


################################################################################
diff  --git a/libcxx/CREDITS.TXT b/libcxx/CREDITS.TXT
index cd5bc08a60fc1..aa3c8cf1a8c6e 100644
--- a/libcxx/CREDITS.TXT
+++ b/libcxx/CREDITS.TXT
@@ -92,6 +92,11 @@ E: stl at microsoft.com
 E: stl at nuwen.net
 D: Implemented floating-point to_chars.
 
+N: Damien Lebrun-Grandie
+E: dalg24 at gmail.com
+E: lebrungrandt at ornl.gov
+D: Implementation of mdspan.
+
 N: Microsoft Corporation
 D: Contributed floating-point to_chars.
 
@@ -149,6 +154,10 @@ N: Stephan Tolksdorf
 E: st at quanttec.com
 D: Minor <atomic> fix
 
+N: Christian Trott
+E: crtrott at sandia.gov
+D: Implementation of mdspan.
+
 N: Ruben Van Boxem
 E: vanboxem dot ruben at gmail dot com
 D: Initial Windows patches.

diff  --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 677a71922bd0b..0f5ee6c24485a 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -332,6 +332,8 @@ Status
     ------------------------------------------------- -----------------
     ``__cpp_lib_is_scoped_enum``                      ``202011L``
     ------------------------------------------------- -----------------
+    ``__cpp_lib_mdspan``                              *unimplemented*
+    ------------------------------------------------- -----------------
     ``__cpp_lib_move_only_function``                  *unimplemented*
     ------------------------------------------------- -----------------
     ``__cpp_lib_optional``                            ``202110L``

diff  --git a/libcxx/docs/Status/Cxx2bPapers.csv b/libcxx/docs/Status/Cxx2bPapers.csv
index a310cfc5ff161..550fb34d4b34e 100644
--- a/libcxx/docs/Status/Cxx2bPapers.csv
+++ b/libcxx/docs/Status/Cxx2bPapers.csv
@@ -51,7 +51,7 @@
 "`P2442R1 <https://wg21.link/P2442R1>`__","LWG","Windowing range adaptors: ``views::chunk`` and ``views::slide``","February 2022","","","|ranges|"
 "`P2443R1 <https://wg21.link/P2443R1>`__","LWG","``views::chunk_by``","February 2022","","","|ranges|"
 "","","","","","",""
-"`P0009R18 <https://wg21.link/P0009R18>`__","LWG","mdspan: A Non-Owning Multidimensional Array Reference","July 2022","",""
+"`P0009R18 <https://wg21.link/P0009R18>`__","LWG","mdspan: A Non-Owning Multidimensional Array Reference","July 2022","|In progress|",""
 "`P0429R9 <https://wg21.link/P0429R9>`__","LWG","A Standard ``flat_map``","July 2022","",""
 "`P1169R4 <https://wg21.link/P1169R4>`__","LWG","``static operator()``","July 2022","|Complete|","16.0"
 "`P1222R4 <https://wg21.link/P1222R4>`__","LWG","A Standard ``flat_set``","July 2022","",""

diff  --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 04293ba4bad6f..190616aaa55cc 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -463,6 +463,7 @@ set(files
   __locale_dir/locale_base_api/bsd_locale_fallbacks.h
   __locale_dir/locale_base_api/locale_guard.h
   __mbstate_t.h
+  __mdspan/extents.h
   __memory/addressof.h
   __memory/align.h
   __memory/aligned_alloc.h
@@ -913,6 +914,7 @@ set(files
   locale.h
   map
   math.h
+  mdspan
   memory
   memory_resource
   mutex

diff  --git a/libcxx/include/__mdspan/extents.h b/libcxx/include/__mdspan/extents.h
new file mode 100644
index 0000000000000..e31e43493f46a
--- /dev/null
+++ b/libcxx/include/__mdspan/extents.h
@@ -0,0 +1,460 @@
+// -*- 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_EXTENTS_H
+#define _LIBCPP___MDSPAN_EXTENTS_H
+
+#include <__assert>
+#include <__config>
+#include <__type_traits/common_type.h>
+#include <__type_traits/is_convertible.h>
+#include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/make_unsigned.h>
+#include <__utility/integer_sequence.h>
+#include <__utility/unreachable.h>
+#include <array>
+#include <cinttypes>
+#include <concepts>
+#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
+
+namespace __mdspan_detail {
+
+// ------------------------------------------------------------------
+// ------------ __static_array --------------------------------------
+// ------------------------------------------------------------------
+// array like class which provides an array of static values with get
+template <class _Tp, _Tp... _Values>
+struct __static_array {
+  static constexpr array<_Tp, sizeof...(_Values)> __array = {_Values...};
+
+public:
+  _LIBCPP_HIDE_FROM_ABI static constexpr size_t __size() { return sizeof...(_Values); }
+  _LIBCPP_HIDE_FROM_ABI static constexpr _Tp __get(size_t __index) noexcept { return __array[__index]; }
+
+  template <size_t _Index>
+  _LIBCPP_HIDE_FROM_ABI static constexpr _Tp __get() {
+    return __get(_Index);
+  }
+};
+
+// ------------------------------------------------------------------
+// ------------ __possibly_empty_array  -----------------------------
+// ------------------------------------------------------------------
+
+// array like class which provides get function and operator [], and
+// has a specialization for the size 0 case.
+// This is needed to make the __maybe_static_array be truly empty, for
+// all static values.
+
+template <class _Tp, size_t _Size>
+struct __possibly_empty_array {
+  _Tp __vals_[_Size];
+  _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator[](size_t __index) { return __vals_[__index]; }
+  _LIBCPP_HIDE_FROM_ABI constexpr const _Tp& operator[](size_t __index) const { return __vals_[__index]; }
+};
+
+template <class _Tp>
+struct __possibly_empty_array<_Tp, 0> {
+  _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator[](size_t) { __libcpp_unreachable(); }
+  _LIBCPP_HIDE_FROM_ABI constexpr const _Tp& operator[](size_t) const { __libcpp_unreachable(); }
+};
+
+// ------------------------------------------------------------------
+// ------------ static_partial_sums ---------------------------------
+// ------------------------------------------------------------------
+
+// Provides a compile time partial sum one can index into
+
+template <size_t... _Values>
+struct __static_partial_sums {
+  _LIBCPP_HIDE_FROM_ABI static constexpr array<size_t, sizeof...(_Values)> __static_partial_sums_impl() {
+    array<size_t, sizeof...(_Values)> __values{_Values...};
+    array<size_t, sizeof...(_Values)> __partial_sums{{}};
+    size_t __running_sum = 0;
+    for (int __i = 0; __i != sizeof...(_Values); ++__i) {
+      __partial_sums[__i] = __running_sum;
+      __running_sum += __values[__i];
+    }
+    return __partial_sums;
+  }
+  static constexpr array<size_t, sizeof...(_Values)> __result{__static_partial_sums_impl()};
+
+  _LIBCPP_HIDE_FROM_ABI static constexpr size_t __get(size_t __index) { return __result[__index]; }
+};
+
+// ------------------------------------------------------------------
+// ------------ __maybe_static_array --------------------------------
+// ------------------------------------------------------------------
+
+// array like class which has a mix of static and runtime values but
+// only stores the runtime values.
+// The type of the static and the runtime values can be 
diff erent.
+// The position of a dynamic value is indicated through a tag value.
+template <class _TDynamic, class _TStatic, _TStatic _DynTag, _TStatic... _Values>
+struct __maybe_static_array {
+  static_assert(is_convertible<_TStatic, _TDynamic>::value,
+                "__maybe_static_array: _TStatic must be convertible to _TDynamic");
+  static_assert(is_convertible<_TDynamic, _TStatic>::value,
+                "__maybe_static_array: _TDynamic must be convertible to _TStatic");
+
+private:
+  // Static values member
+  static constexpr size_t __size_         = sizeof...(_Values);
+  static constexpr size_t __size_dynamic_ = ((_Values == _DynTag) + ... + 0);
+  using _StaticValues                     = __static_array<_TStatic, _Values...>;
+  using _DynamicValues                    = __possibly_empty_array<_TDynamic, __size_dynamic_>;
+
+  // Dynamic values member
+  _LIBCPP_NO_UNIQUE_ADDRESS _DynamicValues __dyn_vals_;
+
+  // static mapping of indices to the position in the dynamic values array
+  using _DynamicIdxMap = __static_partial_sums<static_cast<size_t>(_Values == _DynTag)...>;
+
+  template <size_t... Indices>
+  _LIBCPP_HIDE_FROM_ABI static constexpr _DynamicValues __zeros(index_sequence<Indices...>) noexcept {
+    return _DynamicValues{((void)Indices, 0)...};
+  }
+
+public:
+  _LIBCPP_HIDE_FROM_ABI constexpr __maybe_static_array() noexcept
+      : __dyn_vals_{__zeros(make_index_sequence<__size_dynamic_>())} {}
+
+  // constructors from dynamic values only -- this covers the case for rank() == 0
+  template <class... _DynVals>
+    requires(sizeof...(_DynVals) == __size_dynamic_)
+  _LIBCPP_HIDE_FROM_ABI constexpr __maybe_static_array(_DynVals... __vals)
+      : __dyn_vals_{static_cast<_TDynamic>(__vals)...} {}
+
+  template <class _Tp, size_t _Size >
+    requires(_Size == __size_dynamic_)
+  _LIBCPP_HIDE_FROM_ABI constexpr __maybe_static_array([[maybe_unused]] const span<_Tp, _Size>& __vals) {
+    if constexpr (_Size > 0) {
+      for (size_t __i = 0; __i < _Size; __i++)
+        __dyn_vals_[__i] = static_cast<_TDynamic>(__vals[__i]);
+    }
+  }
+
+  // constructors from all values -- here rank will be greater than 0
+  template <class... _DynVals>
+    requires(sizeof...(_DynVals) != __size_dynamic_)
+  _LIBCPP_HIDE_FROM_ABI constexpr __maybe_static_array(_DynVals... __vals) {
+    static_assert((sizeof...(_DynVals) == __size_), "Invalid number of values.");
+    _TDynamic __values[__size_] = {static_cast<_TDynamic>(__vals)...};
+    for (size_t __i = 0; __i < __size_; __i++) {
+      _TStatic __static_val = _StaticValues::__get(__i);
+      if (__static_val == _DynTag) {
+        __dyn_vals_[_DynamicIdxMap::__get(__i)] = __values[__i];
+      }
+      // Precondition check
+      else
+        _LIBCPP_ASSERT(__values[__i] == static_cast<_TDynamic>(__static_val),
+                       "extents construction: mismatch of provided arguments with static extents.");
+    }
+  }
+
+  template <class _Tp, size_t _Size>
+    requires(_Size != __size_dynamic_)
+  _LIBCPP_HIDE_FROM_ABI constexpr __maybe_static_array(const span<_Tp, _Size>& __vals) {
+    static_assert((_Size == __size_) || (__size_ == dynamic_extent));
+    for (size_t __i = 0; __i < __size_; __i++) {
+      _TStatic __static_val = _StaticValues::__get(__i);
+      if (__static_val == _DynTag) {
+        __dyn_vals_[_DynamicIdxMap::__get(__i)] = static_cast<_TDynamic>(__vals[__i]);
+      }
+      // Precondition check
+      else
+        _LIBCPP_ASSERT(static_cast<_TDynamic>(__vals[__i]) == static_cast<_TDynamic>(__static_val),
+                       "extents construction: mismatch of provided arguments with static extents.");
+    }
+  }
+
+  // access functions
+  _LIBCPP_HIDE_FROM_ABI static constexpr _TStatic __static_value(size_t __i) noexcept {
+    _LIBCPP_ASSERT(__i < __size_, "extents access: index must be less than rank");
+    return _StaticValues::__get(__i);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr _TDynamic __value(size_t __i) const {
+    _LIBCPP_ASSERT(__i < __size_, "extents access: index must be less than rank");
+    _TStatic __static_val = _StaticValues::__get(__i);
+    return __static_val == _DynTag ? __dyn_vals_[_DynamicIdxMap::__get(__i)] : static_cast<_TDynamic>(__static_val);
+  }
+  _LIBCPP_HIDE_FROM_ABI constexpr _TDynamic operator[](size_t __i) const {
+    _LIBCPP_ASSERT(__i < __size_, "extents access: index must be less than rank");
+    return __value(__i);
+  }
+
+  // observers
+  _LIBCPP_HIDE_FROM_ABI static constexpr size_t __size() { return __size_; }
+  _LIBCPP_HIDE_FROM_ABI static constexpr size_t __size_dynamic() { return __size_dynamic_; }
+};
+
+// Function to check whether a value is representable as another type
+// value must be a positive integer otherwise returns false
+// if _From is not an integral, we just check positivity
+template <integral _To, class _From>
+  requires(is_integral_v<_From>)
+_LIBCPP_HIDE_FROM_ABI constexpr bool __is_representable_as(_From __value) {
+  using _To_u   = make_unsigned_t<_To>;
+  using _From_u = make_unsigned_t<_From>;
+  if constexpr (is_signed_v<_From>) {
+    if (__value < 0)
+      return false;
+  }
+  if constexpr (static_cast<_To_u>(numeric_limits<_To>::max()) >= static_cast<_From_u>(numeric_limits<_From>::max())) {
+    return true;
+  } else {
+    return static_cast<_To_u>(numeric_limits<_To>::max()) >= static_cast<_From_u>(__value);
+  }
+}
+
+template <integral _To, class _From>
+  requires(!is_integral_v<_From>)
+_LIBCPP_HIDE_FROM_ABI constexpr bool __is_representable_as(_From __value) {
+  if constexpr (is_signed_v<_To>) {
+    if (static_cast<_To>(__value) < 0)
+      return false;
+  }
+  return true;
+}
+
+template <integral _To, class... _From>
+_LIBCPP_HIDE_FROM_ABI constexpr bool __are_representable_as(_From... __values) {
+  return (__mdspan_detail::__is_representable_as<_To>(__values) && ... && true);
+}
+
+template <integral _To, class _From, size_t _Size>
+_LIBCPP_HIDE_FROM_ABI constexpr bool __are_representable_as(span<_From, _Size> __values) {
+  for (size_t __i = 0; __i < _Size; __i++)
+    if (!__mdspan_detail::__is_representable_as<_To>(__values[__i]))
+      return false;
+  return true;
+}
+
+} // namespace __mdspan_detail
+
+// ------------------------------------------------------------------
+// ------------ extents ---------------------------------------------
+// ------------------------------------------------------------------
+
+// Class to describe the extents of a multi dimensional array.
+// Used by mdspan, mdarray and layout mappings.
+// See ISO C++ standard [mdspan.extents]
+
+template <class _IndexType, size_t... _Extents>
+class extents {
+public:
+  // typedefs for integral types used
+  using index_type = _IndexType;
+  using size_type  = make_unsigned_t<index_type>;
+  using rank_type  = size_t;
+
+  static_assert(is_integral<index_type>::value && !is_same<index_type, bool>::value,
+                "extents::index_type must be a signed or unsigned integer type");
+  static_assert(((__mdspan_detail::__is_representable_as<index_type>(_Extents) || (_Extents == dynamic_extent)) && ...),
+                "extents ctor: arguments must be representable as index_type and nonnegative");
+
+private:
+  static constexpr rank_type __rank_         = sizeof...(_Extents);
+  static constexpr rank_type __rank_dynamic_ = ((_Extents == dynamic_extent) + ... + 0);
+
+  // internal storage type using __maybe_static_array
+  using _Values = __mdspan_detail::__maybe_static_array<_IndexType, size_t, dynamic_extent, _Extents...>;
+  [[no_unique_address]] _Values __vals_;
+
+public:
+  // [mdspan.extents.obs], observers of multidimensional index space
+  _LIBCPP_HIDE_FROM_ABI static constexpr rank_type rank() noexcept { return __rank_; }
+  _LIBCPP_HIDE_FROM_ABI static constexpr rank_type rank_dynamic() noexcept { return __rank_dynamic_; }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr index_type extent(rank_type __r) const noexcept { return __vals_.__value(__r); }
+  _LIBCPP_HIDE_FROM_ABI static constexpr size_t static_extent(rank_type __r) noexcept {
+    return _Values::__static_value(__r);
+  }
+
+  // [mdspan.extents.cons], constructors
+  _LIBCPP_HIDE_FROM_ABI constexpr extents() noexcept = default;
+
+  // Construction from just dynamic or all values.
+  // Precondition check is deferred to __maybe_static_array constructor
+  template <class... _OtherIndexTypes>
+    requires((is_convertible_v<_OtherIndexTypes, index_type> && ...) &&
+             (is_nothrow_constructible_v<index_type, _OtherIndexTypes> && ...) &&
+             (sizeof...(_OtherIndexTypes) == __rank_ || sizeof...(_OtherIndexTypes) == __rank_dynamic_))
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit extents(_OtherIndexTypes... __dynvals) noexcept
+      : __vals_(static_cast<index_type>(__dynvals)...) {
+    _LIBCPP_ASSERT(__mdspan_detail::__are_representable_as<index_type>(__dynvals...),
+                   "extents ctor: arguments must be representable as index_type and nonnegative");
+  }
+
+  template <class _OtherIndexType, size_t _Size>
+    requires(is_convertible_v<_OtherIndexType, index_type> && is_nothrow_constructible_v<index_type, _OtherIndexType> &&
+             (_Size == __rank_ || _Size == __rank_dynamic_))
+  explicit(_Size != __rank_dynamic_)
+      _LIBCPP_HIDE_FROM_ABI constexpr extents(const array<_OtherIndexType, _Size>& __exts) noexcept
+      : __vals_(span(__exts)) {
+    _LIBCPP_ASSERT(__mdspan_detail::__are_representable_as<index_type>(span(__exts)),
+                   "extents ctor: arguments must be representable as index_type and nonnegative");
+  }
+
+  template <class _OtherIndexType, size_t _Size>
+    requires(is_convertible_v<_OtherIndexType, index_type> && is_nothrow_constructible_v<index_type, _OtherIndexType> &&
+             (_Size == __rank_ || _Size == __rank_dynamic_))
+  explicit(_Size != __rank_dynamic_)
+      _LIBCPP_HIDE_FROM_ABI constexpr extents(const span<_OtherIndexType, _Size>& __exts) noexcept
+      : __vals_(__exts) {
+    _LIBCPP_ASSERT(__mdspan_detail::__are_representable_as<index_type>(__exts),
+                   "extents ctor: arguments must be representable as index_type and nonnegative");
+  }
+
+private:
+  // Function to construct extents storage from other extents.
+  template <size_t _DynCount, size_t _Idx, class _OtherExtents, class... _DynamicValues>
+    requires(_Idx < __rank_)
+  _LIBCPP_HIDE_FROM_ABI constexpr _Values __construct_vals_from_extents(
+      integral_constant<size_t, _DynCount>,
+      integral_constant<size_t, _Idx>,
+      const _OtherExtents& __exts,
+      _DynamicValues... __dynamic_values) noexcept {
+    if constexpr (static_extent(_Idx) == dynamic_extent)
+      return __construct_vals_from_extents(
+          integral_constant<size_t, _DynCount + 1>(),
+          integral_constant<size_t, _Idx + 1>(),
+          __exts,
+          __dynamic_values...,
+          __exts.extent(_Idx));
+    else
+      return __construct_vals_from_extents(
+          integral_constant<size_t, _DynCount>(), integral_constant<size_t, _Idx + 1>(), __exts, __dynamic_values...);
+  }
+
+  template <size_t _DynCount, size_t _Idx, class _OtherExtents, class... _DynamicValues>
+    requires((_Idx == __rank_) && (_DynCount == __rank_dynamic_))
+  _LIBCPP_HIDE_FROM_ABI constexpr _Values __construct_vals_from_extents(
+      integral_constant<size_t, _DynCount>,
+      integral_constant<size_t, _Idx>,
+      const _OtherExtents&,
+      _DynamicValues... __dynamic_values) noexcept {
+    return _Values{static_cast<index_type>(__dynamic_values)...};
+  }
+
+public:
+  // Converting constructor from other extents specializations
+  template <class _OtherIndexType, size_t... _OtherExtents>
+    requires((sizeof...(_OtherExtents) == sizeof...(_Extents)) &&
+             ((_OtherExtents == dynamic_extent || _Extents == dynamic_extent || _OtherExtents == _Extents) && ...))
+  explicit((((_Extents != dynamic_extent) && (_OtherExtents == dynamic_extent)) || ...) ||
+           (static_cast<make_unsigned_t<index_type>>(numeric_limits<index_type>::max()) <
+            static_cast<make_unsigned_t<_OtherIndexType>>(numeric_limits<_OtherIndexType>::max())))
+      _LIBCPP_HIDE_FROM_ABI constexpr extents(const extents<_OtherIndexType, _OtherExtents...>& __other) noexcept
+      : __vals_(
+            __construct_vals_from_extents(integral_constant<size_t, 0>(), integral_constant<size_t, 0>(), __other)) {
+    if constexpr (rank() > 0) {
+      for (size_t __r = 0; __r < rank(); __r++) {
+        if constexpr (static_cast<make_unsigned_t<index_type>>(numeric_limits<index_type>::max()) <
+                      static_cast<make_unsigned_t<_OtherIndexType>>(numeric_limits<_OtherIndexType>::max())) {
+          _LIBCPP_ASSERT(__mdspan_detail::__is_representable_as<index_type>(__other.extent(__r)),
+                         "extents ctor: arguments must be representable as index_type and nonnegative");
+        }
+        _LIBCPP_ASSERT(
+            (_Values::__static_value(__r) == dynamic_extent) ||
+                (static_cast<index_type>(__other.extent(__r)) == static_cast<index_type>(_Values::__static_value(__r))),
+            "extents construction: mismatch of provided arguments with static extents.");
+      }
+    }
+  }
+
+  // Comparison operator
+  template <class _OtherIndexType, size_t... _OtherExtents>
+  _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+  operator==(const extents& __lhs, const extents<_OtherIndexType, _OtherExtents...>& __rhs) noexcept {
+    if constexpr (rank() != sizeof...(_OtherExtents)) {
+      return false;
+    } else {
+      for (rank_type __r = 0; __r < __rank_; __r++) {
+        // avoid warning when comparing signed and unsigner integers and pick the wider of two types
+        using _CommonType = common_type_t<index_type, _OtherIndexType>;
+        if (static_cast<_CommonType>(__lhs.extent(__r)) != static_cast<_CommonType>(__rhs.extent(__r))) {
+          return false;
+        }
+      }
+    }
+    return true;
+  }
+};
+
+// Recursive helper classes to implement dextents alias for extents
+namespace __mdspan_detail {
+
+template <class _IndexType, size_t _Rank, class _Extents = extents<_IndexType>>
+struct __make_dextents;
+
+template <class _IndexType, size_t _Rank, size_t... _ExtentsPack>
+struct __make_dextents< _IndexType, _Rank, extents<_IndexType, _ExtentsPack...>> {
+  using type =
+      typename __make_dextents< _IndexType, _Rank - 1, extents<_IndexType, dynamic_extent, _ExtentsPack...>>::type;
+};
+
+template <class _IndexType, size_t... _ExtentsPack>
+struct __make_dextents< _IndexType, 0, extents<_IndexType, _ExtentsPack...>> {
+  using type = extents<_IndexType, _ExtentsPack...>;
+};
+
+} // end namespace __mdspan_detail
+
+// [mdspan.extents.dextents], alias template
+template <class _IndexType, size_t _Rank>
+using dextents = typename __mdspan_detail::__make_dextents<_IndexType, _Rank>::type;
+
+// Deduction guide for extents
+template <class... _IndexTypes>
+extents(_IndexTypes...) -> extents<size_t, size_t((_IndexTypes(), dynamic_extent))...>;
+
+// Helper type traits for identifying a class as extents.
+namespace __mdspan_detail {
+
+template <class _Tp>
+struct __is_extents : false_type {};
+
+template <class _IndexType, size_t... _ExtentsPack>
+struct __is_extents<extents<_IndexType, _ExtentsPack...>> : true_type {};
+
+template <class _Tp>
+inline constexpr bool __is_extents_v = __is_extents<_Tp>::value;
+
+} // namespace __mdspan_detail
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___MDSPAN_EXTENTS_H

diff  --git a/libcxx/include/libcxx.imp b/libcxx/include/libcxx.imp
index 271812e9eb9d4..8414773228bb1 100644
--- a/libcxx/include/libcxx.imp
+++ b/libcxx/include/libcxx.imp
@@ -33,6 +33,7 @@
   { include: [ "@<__ios/.*>", "private", "<ios>", "public" ] },
   { include: [ "@<__iterator/.*>", "private", "<iterator>", "public" ] },
   { include: [ "@<__locale_dir/.*>", "private", "<locale>", "public" ] },
+  { include: [ "@<__mdspan/.*>", "private", "<mdspan>", "public" ] },
   { include: [ "@<__memory/.*>", "private", "<memory>", "public" ] },
   { include: [ "@<__memory_resource/.*>", "private", "<memory_resource>", "public" ] },
   { include: [ "@<__mutex/.*>", "private", "<mutex>", "public" ] },

diff  --git a/libcxx/include/mdspan b/libcxx/include/mdspan
new file mode 100644
index 0000000000000..16b4e3e1641a4
--- /dev/null
+++ b/libcxx/include/mdspan
@@ -0,0 +1,69 @@
+// -*-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
+//
+//===---------------------------------------------------------------------===//
+
+/*
+    extents synopsis
+
+namespace std {
+  template<class _IndexType, size_t... _Extents>
+  class extents {
+  public:
+    using index_type = _IndexType;
+    using size_type = make_unsigned_t<index_type>;
+    using rank_type = size_t;
+
+    // [mdspan.extents.obs], observers of the multidimensional index space
+    static constexpr rank_type rank() noexcept { return sizeof...(_Extents); }
+    static constexpr rank_type rank_dynamic() noexcept { return dynamic-index(rank()); }
+    static constexpr size_t static_extent(rank_type) noexcept;
+    constexpr index_type extent(rank_type) const noexcept;
+
+    // [mdspan.extents.cons], constructors
+    constexpr extents() noexcept = default;
+
+    template<class _OtherIndexType, size_t... _OtherExtents>
+      constexpr explicit(see below)
+        extents(const extents<_OtherIndexType, _OtherExtents...>&) noexcept;
+    template<class... _OtherIndexTypes>
+      constexpr explicit extents(_OtherIndexTypes...) noexcept;
+    template<class _OtherIndexType, size_t N>
+      constexpr explicit(N != rank_dynamic())
+        extents(span<_OtherIndexType, N>) noexcept;
+    template<class _OtherIndexType, size_t N>
+      constexpr explicit(N != rank_dynamic())
+        extents(const array<_OtherIndexType, N>&) noexcept;
+
+    // [mdspan.extents.cmp], comparison operators
+    template<class _OtherIndexType, size_t... _OtherExtents>
+      friend constexpr bool operator==(const extents&,
+                                       const extents<_OtherIndexType, _OtherExtents...>&) noexcept;
+
+  private:
+    // libcxx note: we do not use an array here, but we need to preserve the as-if behavior
+    // for example the default constructor must zero initialize dynamic extents
+    array<index_type, rank_dynamic()> dynamic-extents{};                // exposition only
+  };
+
+  template<class... Integrals>
+    explicit extents(Integrals...)
+      -> see below;
+}
+*/
+
+#ifndef _LIBCPP_MDSPAN
+#define _LIBCPP_MDSPAN
+
+#include <__config>
+#include <__mdspan/extents.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+#endif // _LIBCPP_MDSPAN

diff  --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index af65d60227af9..7bde1becba5d7 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1162,6 +1162,16 @@ module std [system] {
     export initializer_list
     export *
   }
+  module mdspan {
+    header "mdspan"
+    export array
+    export span
+    export *
+
+    module __mdspan {
+      module extents                         { private header "__mdspan/extents.h" }
+    }
+  }
   module memory {
     header "memory"
     export *

diff  --git a/libcxx/include/version b/libcxx/include/version
index f58b51e49feeb..b87abdfce5226 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -123,6 +123,7 @@ __cpp_lib_make_unique                                   201304L <memory>
 __cpp_lib_map_try_emplace                               201411L <map>
 __cpp_lib_math_constants                                201907L <numbers>
 __cpp_lib_math_special_functions                        201603L <cmath>
+__cpp_lib_mdspan                                        202207L <mdspan>
 __cpp_lib_memory_resource                               201603L <memory_resource>
 __cpp_lib_move_iterator_concept                         202207L <iterator>
 __cpp_lib_move_only_function                            202110L <functional>
@@ -408,6 +409,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # define __cpp_lib_forward_like                         202207L
 # define __cpp_lib_invoke_r                             202106L
 # define __cpp_lib_is_scoped_enum                       202011L
+// # define __cpp_lib_mdspan                               202207L
 // # define __cpp_lib_move_only_function                   202110L
 # undef  __cpp_lib_optional
 # define __cpp_lib_optional                             202110L

diff  --git a/libcxx/test/libcxx/assertions/headers_declare_verbose_abort.sh.cpp b/libcxx/test/libcxx/assertions/headers_declare_verbose_abort.sh.cpp
index 1f2a4650c9d52..5587e21a131ad 100644
--- a/libcxx/test/libcxx/assertions/headers_declare_verbose_abort.sh.cpp
+++ b/libcxx/test/libcxx/assertions/headers_declare_verbose_abort.sh.cpp
@@ -418,330 +418,336 @@ int main(int, char**) { return 0; }
 
 // RUN: %{build} -DTEST_71
 #if defined(TEST_71)
-#   include <memory>
+#   include <mdspan>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_72
 #if defined(TEST_72)
-#   include <memory_resource>
+#   include <memory>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_73
-#if defined(TEST_73) && !defined(_LIBCPP_HAS_NO_THREADS)
-#   include <mutex>
+#if defined(TEST_73)
+#   include <memory_resource>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_74
-#if defined(TEST_74)
-#   include <new>
+#if defined(TEST_74) && !defined(_LIBCPP_HAS_NO_THREADS)
+#   include <mutex>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_75
 #if defined(TEST_75)
-#   include <numbers>
+#   include <new>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_76
 #if defined(TEST_76)
-#   include <numeric>
+#   include <numbers>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_77
 #if defined(TEST_77)
-#   include <optional>
+#   include <numeric>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_78
-#if defined(TEST_78) && !defined(_LIBCPP_HAS_NO_LOCALIZATION)
-#   include <ostream>
+#if defined(TEST_78)
+#   include <optional>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_79
-#if defined(TEST_79)
-#   include <queue>
+#if defined(TEST_79) && !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+#   include <ostream>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_80
 #if defined(TEST_80)
-#   include <random>
+#   include <queue>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_81
 #if defined(TEST_81)
-#   include <ranges>
+#   include <random>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_82
 #if defined(TEST_82)
-#   include <ratio>
+#   include <ranges>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_83
-#if defined(TEST_83) && !defined(_LIBCPP_HAS_NO_LOCALIZATION)
-#   include <regex>
+#if defined(TEST_83)
+#   include <ratio>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_84
-#if defined(TEST_84)
-#   include <scoped_allocator>
+#if defined(TEST_84) && !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+#   include <regex>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_85
-#if defined(TEST_85) && !defined(_LIBCPP_HAS_NO_THREADS)
-#   include <semaphore>
+#if defined(TEST_85)
+#   include <scoped_allocator>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_86
-#if defined(TEST_86)
-#   include <set>
+#if defined(TEST_86) && !defined(_LIBCPP_HAS_NO_THREADS)
+#   include <semaphore>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
-// RUN: %{build} -DTEST_88
-#if defined(TEST_88) && !defined(_LIBCPP_HAS_NO_THREADS)
-#   include <shared_mutex>
+// RUN: %{build} -DTEST_87
+#if defined(TEST_87)
+#   include <set>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_89
-#if defined(TEST_89)
-#   include <source_location>
+#if defined(TEST_89) && !defined(_LIBCPP_HAS_NO_THREADS)
+#   include <shared_mutex>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_90
 #if defined(TEST_90)
-#   include <span>
+#   include <source_location>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_91
-#if defined(TEST_91) && !defined(_LIBCPP_HAS_NO_LOCALIZATION)
-#   include <sstream>
+#if defined(TEST_91)
+#   include <span>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_92
-#if defined(TEST_92)
+#if defined(TEST_92) && !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+#   include <sstream>
+    using HandlerType = decltype(std::__libcpp_verbose_abort);
+#endif
+
+// RUN: %{build} -DTEST_93
+#if defined(TEST_93)
 #   include <stack>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
-// RUN: %{build} -DTEST_96
-#if defined(TEST_96)
+// RUN: %{build} -DTEST_97
+#if defined(TEST_97)
 #   include <stdexcept>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
-// RUN: %{build} -DTEST_100
-#if defined(TEST_100) && !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+// RUN: %{build} -DTEST_101
+#if defined(TEST_101) && !defined(_LIBCPP_HAS_NO_LOCALIZATION)
 #   include <streambuf>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
-// RUN: %{build} -DTEST_101
-#if defined(TEST_101)
+// RUN: %{build} -DTEST_102
+#if defined(TEST_102)
 #   include <string>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
-// RUN: %{build} -DTEST_103
-#if defined(TEST_103)
+// RUN: %{build} -DTEST_104
+#if defined(TEST_104)
 #   include <string_view>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
-// RUN: %{build} -DTEST_104
-#if defined(TEST_104) && !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+// RUN: %{build} -DTEST_105
+#if defined(TEST_105) && !defined(_LIBCPP_HAS_NO_LOCALIZATION)
 #   include <strstream>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
-// RUN: %{build} -DTEST_105
-#if defined(TEST_105)
+// RUN: %{build} -DTEST_106
+#if defined(TEST_106)
 #   include <system_error>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
-// RUN: %{build} -DTEST_107
-#if defined(TEST_107) && !defined(_LIBCPP_HAS_NO_THREADS)
-#   include <thread>
-    using HandlerType = decltype(std::__libcpp_verbose_abort);
-#endif
-
 // RUN: %{build} -DTEST_108
-#if defined(TEST_108)
-#   include <tuple>
+#if defined(TEST_108) && !defined(_LIBCPP_HAS_NO_THREADS)
+#   include <thread>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_109
 #if defined(TEST_109)
-#   include <type_traits>
+#   include <tuple>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_110
 #if defined(TEST_110)
-#   include <typeindex>
+#   include <type_traits>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_111
 #if defined(TEST_111)
-#   include <typeinfo>
+#   include <typeindex>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
-// RUN: %{build} -DTEST_113
-#if defined(TEST_113)
-#   include <unordered_map>
+// RUN: %{build} -DTEST_112
+#if defined(TEST_112)
+#   include <typeinfo>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_114
 #if defined(TEST_114)
-#   include <unordered_set>
+#   include <unordered_map>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_115
 #if defined(TEST_115)
-#   include <utility>
+#   include <unordered_set>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_116
 #if defined(TEST_116)
-#   include <valarray>
+#   include <utility>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_117
 #if defined(TEST_117)
-#   include <variant>
+#   include <valarray>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_118
 #if defined(TEST_118)
-#   include <vector>
+#   include <variant>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_119
 #if defined(TEST_119)
-#   include <version>
+#   include <vector>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
-// RUN: %{build} -DTEST_122
-#if defined(TEST_122) && __cplusplus >= 201103L
-#   include <experimental/deque>
+// RUN: %{build} -DTEST_120
+#if defined(TEST_120)
+#   include <version>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_123
 #if defined(TEST_123) && __cplusplus >= 201103L
-#   include <experimental/forward_list>
+#   include <experimental/deque>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_124
 #if defined(TEST_124) && __cplusplus >= 201103L
-#   include <experimental/iterator>
+#   include <experimental/forward_list>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_125
 #if defined(TEST_125) && __cplusplus >= 201103L
-#   include <experimental/list>
+#   include <experimental/iterator>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_126
 #if defined(TEST_126) && __cplusplus >= 201103L
-#   include <experimental/map>
+#   include <experimental/list>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_127
 #if defined(TEST_127) && __cplusplus >= 201103L
-#   include <experimental/memory_resource>
+#   include <experimental/map>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_128
 #if defined(TEST_128) && __cplusplus >= 201103L
-#   include <experimental/propagate_const>
+#   include <experimental/memory_resource>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_129
-#if defined(TEST_129) && !defined(_LIBCPP_HAS_NO_LOCALIZATION) && __cplusplus >= 201103L
-#   include <experimental/regex>
+#if defined(TEST_129) && __cplusplus >= 201103L
+#   include <experimental/propagate_const>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_130
-#if defined(TEST_130) && __cplusplus >= 201103L
-#   include <experimental/set>
+#if defined(TEST_130) && !defined(_LIBCPP_HAS_NO_LOCALIZATION) && __cplusplus >= 201103L
+#   include <experimental/regex>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_131
 #if defined(TEST_131) && __cplusplus >= 201103L
-#   include <experimental/simd>
+#   include <experimental/set>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_132
 #if defined(TEST_132) && __cplusplus >= 201103L
-#   include <experimental/string>
+#   include <experimental/simd>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_133
 #if defined(TEST_133) && __cplusplus >= 201103L
-#   include <experimental/type_traits>
+#   include <experimental/string>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_134
 #if defined(TEST_134) && __cplusplus >= 201103L
-#   include <experimental/unordered_map>
+#   include <experimental/type_traits>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_135
 #if defined(TEST_135) && __cplusplus >= 201103L
-#   include <experimental/unordered_set>
+#   include <experimental/unordered_map>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_136
 #if defined(TEST_136) && __cplusplus >= 201103L
-#   include <experimental/utility>
+#   include <experimental/unordered_set>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif
 
 // RUN: %{build} -DTEST_137
 #if defined(TEST_137) && __cplusplus >= 201103L
+#   include <experimental/utility>
+    using HandlerType = decltype(std::__libcpp_verbose_abort);
+#endif
+
+// RUN: %{build} -DTEST_138
+#if defined(TEST_138) && __cplusplus >= 201103L
 #   include <experimental/vector>
     using HandlerType = decltype(std::__libcpp_verbose_abort);
 #endif

diff  --git a/libcxx/test/libcxx/clang_tidy.sh.cpp b/libcxx/test/libcxx/clang_tidy.sh.cpp
index ab9bbc6c39144..c4bce6b155f3e 100644
--- a/libcxx/test/libcxx/clang_tidy.sh.cpp
+++ b/libcxx/test/libcxx/clang_tidy.sh.cpp
@@ -134,6 +134,7 @@ END-SCRIPT
 #endif
 #include <map>
 #include <math.h>
+#include <mdspan>
 #include <memory>
 #include <memory_resource>
 #if !defined(_LIBCPP_HAS_NO_THREADS)

diff  --git a/libcxx/test/libcxx/double_include.sh.cpp b/libcxx/test/libcxx/double_include.sh.cpp
index 292f53a0d077a..7f61fef65662c 100644
--- a/libcxx/test/libcxx/double_include.sh.cpp
+++ b/libcxx/test/libcxx/double_include.sh.cpp
@@ -132,6 +132,7 @@ END-SCRIPT
 #endif
 #include <map>
 #include <math.h>
+#include <mdspan>
 #include <memory>
 #include <memory_resource>
 #if !defined(_LIBCPP_HAS_NO_THREADS)

diff  --git a/libcxx/test/libcxx/min_max_macros.compile.pass.cpp b/libcxx/test/libcxx/min_max_macros.compile.pass.cpp
index d032e97f1ddc9..0bd4c4791b299 100644
--- a/libcxx/test/libcxx/min_max_macros.compile.pass.cpp
+++ b/libcxx/test/libcxx/min_max_macros.compile.pass.cpp
@@ -203,6 +203,8 @@ TEST_MACROS();
 TEST_MACROS();
 #include <math.h>
 TEST_MACROS();
+#include <mdspan>
+TEST_MACROS();
 #include <memory>
 TEST_MACROS();
 #include <memory_resource>

diff  --git a/libcxx/test/libcxx/modules_include.sh.cpp b/libcxx/test/libcxx/modules_include.sh.cpp
index 3f3967aa418bb..8a625890f760e 100644
--- a/libcxx/test/libcxx/modules_include.sh.cpp
+++ b/libcxx/test/libcxx/modules_include.sh.cpp
@@ -465,405 +465,410 @@ END-SCRIPT
 // RUN: echo 'TEST_71=$!' >> %t.sh
 // RUN: echo "wait $TEST_55" >> %t.sh
 #if defined(TEST_71)
-#include <memory>
+#include <mdspan>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_72 &' >> %t.sh
 // RUN: echo 'TEST_72=$!' >> %t.sh
 // RUN: echo "wait $TEST_56" >> %t.sh
 #if defined(TEST_72)
-#include <memory_resource>
+#include <memory>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_73 &' >> %t.sh
 // RUN: echo 'TEST_73=$!' >> %t.sh
 // RUN: echo "wait $TEST_57" >> %t.sh
-#if defined(TEST_73) && !defined(_LIBCPP_HAS_NO_THREADS)
-#include <mutex>
+#if defined(TEST_73)
+#include <memory_resource>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_74 &' >> %t.sh
 // RUN: echo 'TEST_74=$!' >> %t.sh
 // RUN: echo "wait $TEST_58" >> %t.sh
-#if defined(TEST_74)
-#include <new>
+#if defined(TEST_74) && !defined(_LIBCPP_HAS_NO_THREADS)
+#include <mutex>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_75 &' >> %t.sh
 // RUN: echo 'TEST_75=$!' >> %t.sh
 // RUN: echo "wait $TEST_59" >> %t.sh
 #if defined(TEST_75)
-#include <numbers>
+#include <new>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_76 &' >> %t.sh
 // RUN: echo 'TEST_76=$!' >> %t.sh
 // RUN: echo "wait $TEST_60" >> %t.sh
 #if defined(TEST_76)
-#include <numeric>
+#include <numbers>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_77 &' >> %t.sh
 // RUN: echo 'TEST_77=$!' >> %t.sh
 // RUN: echo "wait $TEST_61" >> %t.sh
 #if defined(TEST_77)
-#include <optional>
+#include <numeric>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_78 &' >> %t.sh
 // RUN: echo 'TEST_78=$!' >> %t.sh
 // RUN: echo "wait $TEST_62" >> %t.sh
-#if defined(TEST_78) && !defined(_LIBCPP_HAS_NO_LOCALIZATION)
-#include <ostream>
+#if defined(TEST_78)
+#include <optional>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_79 &' >> %t.sh
 // RUN: echo 'TEST_79=$!' >> %t.sh
 // RUN: echo "wait $TEST_63" >> %t.sh
-#if defined(TEST_79)
-#include <queue>
+#if defined(TEST_79) && !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+#include <ostream>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_80 &' >> %t.sh
 // RUN: echo 'TEST_80=$!' >> %t.sh
 // RUN: echo "wait $TEST_64" >> %t.sh
 #if defined(TEST_80)
-#include <random>
+#include <queue>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_81 &' >> %t.sh
 // RUN: echo 'TEST_81=$!' >> %t.sh
 // RUN: echo "wait $TEST_65" >> %t.sh
 #if defined(TEST_81)
-#include <ranges>
+#include <random>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_82 &' >> %t.sh
 // RUN: echo 'TEST_82=$!' >> %t.sh
 // RUN: echo "wait $TEST_66" >> %t.sh
 #if defined(TEST_82)
-#include <ratio>
+#include <ranges>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_83 &' >> %t.sh
 // RUN: echo 'TEST_83=$!' >> %t.sh
 // RUN: echo "wait $TEST_67" >> %t.sh
-#if defined(TEST_83) && !defined(_LIBCPP_HAS_NO_LOCALIZATION)
-#include <regex>
+#if defined(TEST_83)
+#include <ratio>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_84 &' >> %t.sh
 // RUN: echo 'TEST_84=$!' >> %t.sh
 // RUN: echo "wait $TEST_68" >> %t.sh
-#if defined(TEST_84)
-#include <scoped_allocator>
+#if defined(TEST_84) && !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+#include <regex>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_85 &' >> %t.sh
 // RUN: echo 'TEST_85=$!' >> %t.sh
 // RUN: echo "wait $TEST_69" >> %t.sh
-#if defined(TEST_85) && !defined(_LIBCPP_HAS_NO_THREADS)
-#include <semaphore>
+#if defined(TEST_85)
+#include <scoped_allocator>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_86 &' >> %t.sh
 // RUN: echo 'TEST_86=$!' >> %t.sh
 // RUN: echo "wait $TEST_70" >> %t.sh
-#if defined(TEST_86)
-#include <set>
+#if defined(TEST_86) && !defined(_LIBCPP_HAS_NO_THREADS)
+#include <semaphore>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_87 &' >> %t.sh
 // RUN: echo 'TEST_87=$!' >> %t.sh
 // RUN: echo "wait $TEST_71" >> %t.sh
 #if defined(TEST_87)
-#include <setjmp.h>
+#include <set>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_88 &' >> %t.sh
 // RUN: echo 'TEST_88=$!' >> %t.sh
 // RUN: echo "wait $TEST_72" >> %t.sh
-#if defined(TEST_88) && !defined(_LIBCPP_HAS_NO_THREADS)
-#include <shared_mutex>
+#if defined(TEST_88)
+#include <setjmp.h>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_89 &' >> %t.sh
 // RUN: echo 'TEST_89=$!' >> %t.sh
 // RUN: echo "wait $TEST_73" >> %t.sh
-#if defined(TEST_89)
-#include <source_location>
+#if defined(TEST_89) && !defined(_LIBCPP_HAS_NO_THREADS)
+#include <shared_mutex>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_90 &' >> %t.sh
 // RUN: echo 'TEST_90=$!' >> %t.sh
 // RUN: echo "wait $TEST_74" >> %t.sh
 #if defined(TEST_90)
-#include <span>
+#include <source_location>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_91 &' >> %t.sh
 // RUN: echo 'TEST_91=$!' >> %t.sh
 // RUN: echo "wait $TEST_75" >> %t.sh
-#if defined(TEST_91) && !defined(_LIBCPP_HAS_NO_LOCALIZATION)
-#include <sstream>
+#if defined(TEST_91)
+#include <span>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_92 &' >> %t.sh
 // RUN: echo 'TEST_92=$!' >> %t.sh
 // RUN: echo "wait $TEST_76" >> %t.sh
-#if defined(TEST_92)
-#include <stack>
+#if defined(TEST_92) && !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+#include <sstream>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_93 &' >> %t.sh
 // RUN: echo 'TEST_93=$!' >> %t.sh
 // RUN: echo "wait $TEST_77" >> %t.sh
-#if defined(TEST_93) && __cplusplus > 202002L && !defined(_LIBCPP_HAS_NO_THREADS)
-#include <stdatomic.h>
+#if defined(TEST_93)
+#include <stack>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_94 &' >> %t.sh
 // RUN: echo 'TEST_94=$!' >> %t.sh
 // RUN: echo "wait $TEST_78" >> %t.sh
-#if defined(TEST_94)
-#include <stdbool.h>
+#if defined(TEST_94) && __cplusplus > 202002L && !defined(_LIBCPP_HAS_NO_THREADS)
+#include <stdatomic.h>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_95 &' >> %t.sh
 // RUN: echo 'TEST_95=$!' >> %t.sh
 // RUN: echo "wait $TEST_79" >> %t.sh
 #if defined(TEST_95)
-#include <stddef.h>
+#include <stdbool.h>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_96 &' >> %t.sh
 // RUN: echo 'TEST_96=$!' >> %t.sh
 // RUN: echo "wait $TEST_80" >> %t.sh
 #if defined(TEST_96)
-#include <stdexcept>
+#include <stddef.h>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_97 &' >> %t.sh
 // RUN: echo 'TEST_97=$!' >> %t.sh
 // RUN: echo "wait $TEST_81" >> %t.sh
 #if defined(TEST_97)
-#include <stdint.h>
+#include <stdexcept>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_98 &' >> %t.sh
 // RUN: echo 'TEST_98=$!' >> %t.sh
 // RUN: echo "wait $TEST_82" >> %t.sh
 #if defined(TEST_98)
-#include <stdio.h>
+#include <stdint.h>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_99 &' >> %t.sh
 // RUN: echo 'TEST_99=$!' >> %t.sh
 // RUN: echo "wait $TEST_83" >> %t.sh
 #if defined(TEST_99)
-#include <stdlib.h>
+#include <stdio.h>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_100 &' >> %t.sh
 // RUN: echo 'TEST_100=$!' >> %t.sh
 // RUN: echo "wait $TEST_84" >> %t.sh
-#if defined(TEST_100) && !defined(_LIBCPP_HAS_NO_LOCALIZATION)
-#include <streambuf>
+#if defined(TEST_100)
+#include <stdlib.h>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_101 &' >> %t.sh
 // RUN: echo 'TEST_101=$!' >> %t.sh
 // RUN: echo "wait $TEST_85" >> %t.sh
-#if defined(TEST_101)
-#include <string>
+#if defined(TEST_101) && !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+#include <streambuf>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_102 &' >> %t.sh
 // RUN: echo 'TEST_102=$!' >> %t.sh
 // RUN: echo "wait $TEST_86" >> %t.sh
 #if defined(TEST_102)
-#include <string.h>
+#include <string>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_103 &' >> %t.sh
 // RUN: echo 'TEST_103=$!' >> %t.sh
 // RUN: echo "wait $TEST_87" >> %t.sh
 #if defined(TEST_103)
-#include <string_view>
+#include <string.h>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_104 &' >> %t.sh
 // RUN: echo 'TEST_104=$!' >> %t.sh
 // RUN: echo "wait $TEST_88" >> %t.sh
-#if defined(TEST_104) && !defined(_LIBCPP_HAS_NO_LOCALIZATION)
-#include <strstream>
+#if defined(TEST_104)
+#include <string_view>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_105 &' >> %t.sh
 // RUN: echo 'TEST_105=$!' >> %t.sh
 // RUN: echo "wait $TEST_89" >> %t.sh
-#if defined(TEST_105)
-#include <system_error>
+#if defined(TEST_105) && !defined(_LIBCPP_HAS_NO_LOCALIZATION)
+#include <strstream>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_106 &' >> %t.sh
 // RUN: echo 'TEST_106=$!' >> %t.sh
 // RUN: echo "wait $TEST_90" >> %t.sh
 #if defined(TEST_106)
-#include <tgmath.h>
+#include <system_error>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_107 &' >> %t.sh
 // RUN: echo 'TEST_107=$!' >> %t.sh
 // RUN: echo "wait $TEST_91" >> %t.sh
-#if defined(TEST_107) && !defined(_LIBCPP_HAS_NO_THREADS)
-#include <thread>
+#if defined(TEST_107)
+#include <tgmath.h>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_108 &' >> %t.sh
 // RUN: echo 'TEST_108=$!' >> %t.sh
 // RUN: echo "wait $TEST_92" >> %t.sh
-#if defined(TEST_108)
-#include <tuple>
+#if defined(TEST_108) && !defined(_LIBCPP_HAS_NO_THREADS)
+#include <thread>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_109 &' >> %t.sh
 // RUN: echo 'TEST_109=$!' >> %t.sh
 // RUN: echo "wait $TEST_93" >> %t.sh
 #if defined(TEST_109)
-#include <type_traits>
+#include <tuple>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_110 &' >> %t.sh
 // RUN: echo 'TEST_110=$!' >> %t.sh
 // RUN: echo "wait $TEST_94" >> %t.sh
 #if defined(TEST_110)
-#include <typeindex>
+#include <type_traits>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_111 &' >> %t.sh
 // RUN: echo 'TEST_111=$!' >> %t.sh
 // RUN: echo "wait $TEST_95" >> %t.sh
 #if defined(TEST_111)
-#include <typeinfo>
+#include <typeindex>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_112 &' >> %t.sh
 // RUN: echo 'TEST_112=$!' >> %t.sh
 // RUN: echo "wait $TEST_96" >> %t.sh
 #if defined(TEST_112)
-#include <uchar.h>
+#include <typeinfo>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_113 &' >> %t.sh
 // RUN: echo 'TEST_113=$!' >> %t.sh
 // RUN: echo "wait $TEST_97" >> %t.sh
 #if defined(TEST_113)
-#include <unordered_map>
+#include <uchar.h>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_114 &' >> %t.sh
 // RUN: echo 'TEST_114=$!' >> %t.sh
 // RUN: echo "wait $TEST_98" >> %t.sh
 #if defined(TEST_114)
-#include <unordered_set>
+#include <unordered_map>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_115 &' >> %t.sh
 // RUN: echo 'TEST_115=$!' >> %t.sh
 // RUN: echo "wait $TEST_99" >> %t.sh
 #if defined(TEST_115)
-#include <utility>
+#include <unordered_set>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_116 &' >> %t.sh
 // RUN: echo 'TEST_116=$!' >> %t.sh
 // RUN: echo "wait $TEST_100" >> %t.sh
 #if defined(TEST_116)
-#include <valarray>
+#include <utility>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_117 &' >> %t.sh
 // RUN: echo 'TEST_117=$!' >> %t.sh
 // RUN: echo "wait $TEST_101" >> %t.sh
 #if defined(TEST_117)
-#include <variant>
+#include <valarray>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_118 &' >> %t.sh
 // RUN: echo 'TEST_118=$!' >> %t.sh
 // RUN: echo "wait $TEST_102" >> %t.sh
 #if defined(TEST_118)
-#include <vector>
+#include <variant>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_119 &' >> %t.sh
 // RUN: echo 'TEST_119=$!' >> %t.sh
 // RUN: echo "wait $TEST_103" >> %t.sh
 #if defined(TEST_119)
-#include <version>
+#include <vector>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_120 &' >> %t.sh
 // RUN: echo 'TEST_120=$!' >> %t.sh
 // RUN: echo "wait $TEST_104" >> %t.sh
-#if defined(TEST_120) && !defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS)
-#include <wchar.h>
+#if defined(TEST_120)
+#include <version>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_121 &' >> %t.sh
 // RUN: echo 'TEST_121=$!' >> %t.sh
 // RUN: echo "wait $TEST_105" >> %t.sh
 #if defined(TEST_121) && !defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS)
-#include <wctype.h>
+#include <wchar.h>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_122 &' >> %t.sh
 // RUN: echo 'TEST_122=$!' >> %t.sh
 // RUN: echo "wait $TEST_106" >> %t.sh
-#if defined(TEST_122) && __cplusplus >= 201103L
-#include <experimental/deque>
+#if defined(TEST_122) && !defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS)
+#include <wctype.h>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_123 &' >> %t.sh
 // RUN: echo 'TEST_123=$!' >> %t.sh
 // RUN: echo "wait $TEST_107" >> %t.sh
 #if defined(TEST_123) && __cplusplus >= 201103L
-#include <experimental/forward_list>
+#include <experimental/deque>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_124 &' >> %t.sh
 // RUN: echo 'TEST_124=$!' >> %t.sh
 // RUN: echo "wait $TEST_108" >> %t.sh
 #if defined(TEST_124) && __cplusplus >= 201103L
-#include <experimental/iterator>
+#include <experimental/forward_list>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_125 &' >> %t.sh
 // RUN: echo 'TEST_125=$!' >> %t.sh
 // RUN: echo "wait $TEST_109" >> %t.sh
 #if defined(TEST_125) && __cplusplus >= 201103L
-#include <experimental/list>
+#include <experimental/iterator>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_126 &' >> %t.sh
 // RUN: echo 'TEST_126=$!' >> %t.sh
 // RUN: echo "wait $TEST_110" >> %t.sh
 #if defined(TEST_126) && __cplusplus >= 201103L
-#include <experimental/map>
+#include <experimental/list>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_127 &' >> %t.sh
 // RUN: echo 'TEST_127=$!' >> %t.sh
 // RUN: echo "wait $TEST_111" >> %t.sh
 #if defined(TEST_127) && __cplusplus >= 201103L
-#include <experimental/memory_resource>
+#include <experimental/map>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_128 &' >> %t.sh
 // RUN: echo 'TEST_128=$!' >> %t.sh
 // RUN: echo "wait $TEST_112" >> %t.sh
 #if defined(TEST_128) && __cplusplus >= 201103L
-#include <experimental/propagate_const>
+#include <experimental/memory_resource>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_129 &' >> %t.sh
 // RUN: echo 'TEST_129=$!' >> %t.sh
 // RUN: echo "wait $TEST_113" >> %t.sh
-#if defined(TEST_129) && !defined(_LIBCPP_HAS_NO_LOCALIZATION) && __cplusplus >= 201103L
-#include <experimental/regex>
+#if defined(TEST_129) && __cplusplus >= 201103L
+#include <experimental/propagate_const>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_130 &' >> %t.sh
 // RUN: echo 'TEST_130=$!' >> %t.sh
 // RUN: echo "wait $TEST_114" >> %t.sh
-#if defined(TEST_130) && __cplusplus >= 201103L
-#include <experimental/set>
+#if defined(TEST_130) && !defined(_LIBCPP_HAS_NO_LOCALIZATION) && __cplusplus >= 201103L
+#include <experimental/regex>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_131 &' >> %t.sh
 // RUN: echo 'TEST_131=$!' >> %t.sh
 // RUN: echo "wait $TEST_115" >> %t.sh
 #if defined(TEST_131) && __cplusplus >= 201103L
-#include <experimental/simd>
+#include <experimental/set>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_132 &' >> %t.sh
 // RUN: echo 'TEST_132=$!' >> %t.sh
 // RUN: echo "wait $TEST_116" >> %t.sh
 #if defined(TEST_132) && __cplusplus >= 201103L
-#include <experimental/string>
+#include <experimental/simd>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_133 &' >> %t.sh
 // RUN: echo 'TEST_133=$!' >> %t.sh
 // RUN: echo "wait $TEST_117" >> %t.sh
 #if defined(TEST_133) && __cplusplus >= 201103L
-#include <experimental/type_traits>
+#include <experimental/string>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_134 &' >> %t.sh
 // RUN: echo 'TEST_134=$!' >> %t.sh
 // RUN: echo "wait $TEST_118" >> %t.sh
 #if defined(TEST_134) && __cplusplus >= 201103L
-#include <experimental/unordered_map>
+#include <experimental/type_traits>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_135 &' >> %t.sh
 // RUN: echo 'TEST_135=$!' >> %t.sh
 // RUN: echo "wait $TEST_119" >> %t.sh
 #if defined(TEST_135) && __cplusplus >= 201103L
-#include <experimental/unordered_set>
+#include <experimental/unordered_map>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_136 &' >> %t.sh
 // RUN: echo 'TEST_136=$!' >> %t.sh
 // RUN: echo "wait $TEST_120" >> %t.sh
 #if defined(TEST_136) && __cplusplus >= 201103L
-#include <experimental/utility>
+#include <experimental/unordered_set>
 #endif
 // RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_137 &' >> %t.sh
 // RUN: echo 'TEST_137=$!' >> %t.sh
 // RUN: echo "wait $TEST_121" >> %t.sh
 #if defined(TEST_137) && __cplusplus >= 201103L
-#include <experimental/vector>
+#include <experimental/utility>
 #endif
+// RUN: echo '%{cxx} %s %{flags} %{compile_flags} -fmodules -fcxx-modules -fmodules-cache-path=%t -fsyntax-only -DTEST_138 &' >> %t.sh
+// RUN: echo 'TEST_138=$!' >> %t.sh
 // RUN: echo "wait $TEST_122" >> %t.sh
+#if defined(TEST_138) && __cplusplus >= 201103L
+#include <experimental/vector>
+#endif
 // RUN: echo "wait $TEST_123" >> %t.sh
 // RUN: echo "wait $TEST_124" >> %t.sh
 // RUN: echo "wait $TEST_125" >> %t.sh
@@ -879,5 +884,6 @@ END-SCRIPT
 // RUN: echo "wait $TEST_135" >> %t.sh
 // RUN: echo "wait $TEST_136" >> %t.sh
 // RUN: echo "wait $TEST_137" >> %t.sh
+// RUN: echo "wait $TEST_138" >> %t.sh
 // RUN: bash %t.sh
 // GENERATED-MARKER

diff  --git a/libcxx/test/libcxx/nasty_macros.compile.pass.cpp b/libcxx/test/libcxx/nasty_macros.compile.pass.cpp
index 5254e0d78e88b..49ab781e87710 100644
--- a/libcxx/test/libcxx/nasty_macros.compile.pass.cpp
+++ b/libcxx/test/libcxx/nasty_macros.compile.pass.cpp
@@ -257,6 +257,7 @@ END-SCRIPT
 #endif
 #include <map>
 #include <math.h>
+#include <mdspan>
 #include <memory>
 #include <memory_resource>
 #if !defined(_LIBCPP_HAS_NO_THREADS)

diff  --git a/libcxx/test/libcxx/no_assert_include.compile.pass.cpp b/libcxx/test/libcxx/no_assert_include.compile.pass.cpp
index 779a21a02dd50..b5ac8519320c0 100644
--- a/libcxx/test/libcxx/no_assert_include.compile.pass.cpp
+++ b/libcxx/test/libcxx/no_assert_include.compile.pass.cpp
@@ -129,6 +129,7 @@ END-SCRIPT
 #endif
 #include <map>
 #include <math.h>
+#include <mdspan>
 #include <memory>
 #include <memory_resource>
 #if !defined(_LIBCPP_HAS_NO_THREADS)

diff  --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp
index 6762913512c72..3ff91e1de1432 100644
--- a/libcxx/test/libcxx/private_headers.verify.cpp
+++ b/libcxx/test/libcxx/private_headers.verify.cpp
@@ -493,6 +493,7 @@ END-SCRIPT
 #include <__iterator/wrap_iter.h> // expected-error@*:* {{use of private header from outside its module: '__iterator/wrap_iter.h'}}
 #include <__locale> // expected-error@*:* {{use of private header from outside its module: '__locale'}}
 #include <__mbstate_t.h> // expected-error@*:* {{use of private header from outside its module: '__mbstate_t.h'}}
+#include <__mdspan/extents.h> // expected-error@*:* {{use of private header from outside its module: '__mdspan/extents.h'}}
 #include <__memory/addressof.h> // expected-error@*:* {{use of private header from outside its module: '__memory/addressof.h'}}
 #include <__memory/align.h> // expected-error@*:* {{use of private header from outside its module: '__memory/align.h'}}
 #include <__memory/aligned_alloc.h> // expected-error@*:* {{use of private header from outside its module: '__memory/aligned_alloc.h'}}

diff  --git a/libcxx/test/libcxx/transitive_includes.sh.cpp b/libcxx/test/libcxx/transitive_includes.sh.cpp
index 1bbc906bb5072..db462025bb1cd 100644
--- a/libcxx/test/libcxx/transitive_includes.sh.cpp
+++ b/libcxx/test/libcxx/transitive_includes.sh.cpp
@@ -317,224 +317,228 @@ END-SCRIPT
 #if defined(TEST_69)
 #include <map>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_71 > /dev/null 2> %t/header.memory
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_71 > /dev/null 2> %t/header.mdspan
 #if defined(TEST_71)
-#include <memory>
+#include <mdspan>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_72 > /dev/null 2> %t/header.memory_resource
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_72 > /dev/null 2> %t/header.memory
 #if defined(TEST_72)
-#include <memory_resource>
+#include <memory>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_73 > /dev/null 2> %t/header.mutex
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_73 > /dev/null 2> %t/header.memory_resource
 #if defined(TEST_73)
-#include <mutex>
+#include <memory_resource>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_74 > /dev/null 2> %t/header.new
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_74 > /dev/null 2> %t/header.mutex
 #if defined(TEST_74)
-#include <new>
+#include <mutex>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_75 > /dev/null 2> %t/header.numbers
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_75 > /dev/null 2> %t/header.new
 #if defined(TEST_75)
-#include <numbers>
+#include <new>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_76 > /dev/null 2> %t/header.numeric
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_76 > /dev/null 2> %t/header.numbers
 #if defined(TEST_76)
-#include <numeric>
+#include <numbers>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_77 > /dev/null 2> %t/header.optional
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_77 > /dev/null 2> %t/header.numeric
 #if defined(TEST_77)
-#include <optional>
+#include <numeric>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_78 > /dev/null 2> %t/header.ostream
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_78 > /dev/null 2> %t/header.optional
 #if defined(TEST_78)
-#include <ostream>
+#include <optional>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_79 > /dev/null 2> %t/header.queue
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_79 > /dev/null 2> %t/header.ostream
 #if defined(TEST_79)
-#include <queue>
+#include <ostream>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_80 > /dev/null 2> %t/header.random
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_80 > /dev/null 2> %t/header.queue
 #if defined(TEST_80)
-#include <random>
+#include <queue>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_81 > /dev/null 2> %t/header.ranges
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_81 > /dev/null 2> %t/header.random
 #if defined(TEST_81)
-#include <ranges>
+#include <random>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_82 > /dev/null 2> %t/header.ratio
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_82 > /dev/null 2> %t/header.ranges
 #if defined(TEST_82)
-#include <ratio>
+#include <ranges>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_83 > /dev/null 2> %t/header.regex
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_83 > /dev/null 2> %t/header.ratio
 #if defined(TEST_83)
-#include <regex>
+#include <ratio>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_84 > /dev/null 2> %t/header.scoped_allocator
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_84 > /dev/null 2> %t/header.regex
 #if defined(TEST_84)
-#include <scoped_allocator>
+#include <regex>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_85 > /dev/null 2> %t/header.semaphore
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_85 > /dev/null 2> %t/header.scoped_allocator
 #if defined(TEST_85)
-#include <semaphore>
+#include <scoped_allocator>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_86 > /dev/null 2> %t/header.set
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_86 > /dev/null 2> %t/header.semaphore
 #if defined(TEST_86)
+#include <semaphore>
+#endif
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_87 > /dev/null 2> %t/header.set
+#if defined(TEST_87)
 #include <set>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_88 > /dev/null 2> %t/header.shared_mutex
-#if defined(TEST_88)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_89 > /dev/null 2> %t/header.shared_mutex
+#if defined(TEST_89)
 #include <shared_mutex>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_89 > /dev/null 2> %t/header.source_location
-#if defined(TEST_89)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_90 > /dev/null 2> %t/header.source_location
+#if defined(TEST_90)
 #include <source_location>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_90 > /dev/null 2> %t/header.span
-#if defined(TEST_90)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_91 > /dev/null 2> %t/header.span
+#if defined(TEST_91)
 #include <span>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_91 > /dev/null 2> %t/header.sstream
-#if defined(TEST_91)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_92 > /dev/null 2> %t/header.sstream
+#if defined(TEST_92)
 #include <sstream>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_92 > /dev/null 2> %t/header.stack
-#if defined(TEST_92)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_93 > /dev/null 2> %t/header.stack
+#if defined(TEST_93)
 #include <stack>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_96 > /dev/null 2> %t/header.stdexcept
-#if defined(TEST_96)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_97 > /dev/null 2> %t/header.stdexcept
+#if defined(TEST_97)
 #include <stdexcept>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_100 > /dev/null 2> %t/header.streambuf
-#if defined(TEST_100)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_101 > /dev/null 2> %t/header.streambuf
+#if defined(TEST_101)
 #include <streambuf>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_101 > /dev/null 2> %t/header.string
-#if defined(TEST_101)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_102 > /dev/null 2> %t/header.string
+#if defined(TEST_102)
 #include <string>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_103 > /dev/null 2> %t/header.string_view
-#if defined(TEST_103)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_104 > /dev/null 2> %t/header.string_view
+#if defined(TEST_104)
 #include <string_view>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_104 > /dev/null 2> %t/header.strstream
-#if defined(TEST_104)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_105 > /dev/null 2> %t/header.strstream
+#if defined(TEST_105)
 #include <strstream>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_105 > /dev/null 2> %t/header.system_error
-#if defined(TEST_105)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_106 > /dev/null 2> %t/header.system_error
+#if defined(TEST_106)
 #include <system_error>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_107 > /dev/null 2> %t/header.thread
-#if defined(TEST_107)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_108 > /dev/null 2> %t/header.thread
+#if defined(TEST_108)
 #include <thread>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_108 > /dev/null 2> %t/header.tuple
-#if defined(TEST_108)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_109 > /dev/null 2> %t/header.tuple
+#if defined(TEST_109)
 #include <tuple>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_109 > /dev/null 2> %t/header.type_traits
-#if defined(TEST_109)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_110 > /dev/null 2> %t/header.type_traits
+#if defined(TEST_110)
 #include <type_traits>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_110 > /dev/null 2> %t/header.typeindex
-#if defined(TEST_110)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_111 > /dev/null 2> %t/header.typeindex
+#if defined(TEST_111)
 #include <typeindex>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_111 > /dev/null 2> %t/header.typeinfo
-#if defined(TEST_111)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_112 > /dev/null 2> %t/header.typeinfo
+#if defined(TEST_112)
 #include <typeinfo>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_113 > /dev/null 2> %t/header.unordered_map
-#if defined(TEST_113)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_114 > /dev/null 2> %t/header.unordered_map
+#if defined(TEST_114)
 #include <unordered_map>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_114 > /dev/null 2> %t/header.unordered_set
-#if defined(TEST_114)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_115 > /dev/null 2> %t/header.unordered_set
+#if defined(TEST_115)
 #include <unordered_set>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_115 > /dev/null 2> %t/header.utility
-#if defined(TEST_115)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_116 > /dev/null 2> %t/header.utility
+#if defined(TEST_116)
 #include <utility>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_116 > /dev/null 2> %t/header.valarray
-#if defined(TEST_116)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_117 > /dev/null 2> %t/header.valarray
+#if defined(TEST_117)
 #include <valarray>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_117 > /dev/null 2> %t/header.variant
-#if defined(TEST_117)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_118 > /dev/null 2> %t/header.variant
+#if defined(TEST_118)
 #include <variant>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_118 > /dev/null 2> %t/header.vector
-#if defined(TEST_118)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_119 > /dev/null 2> %t/header.vector
+#if defined(TEST_119)
 #include <vector>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_119 > /dev/null 2> %t/header.version
-#if defined(TEST_119)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_120 > /dev/null 2> %t/header.version
+#if defined(TEST_120)
 #include <version>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_122 > /dev/null 2> %t/header.experimental_deque
-#if defined(TEST_122)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_123 > /dev/null 2> %t/header.experimental_deque
+#if defined(TEST_123)
 #include <experimental/deque>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_123 > /dev/null 2> %t/header.experimental_forward_list
-#if defined(TEST_123)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_124 > /dev/null 2> %t/header.experimental_forward_list
+#if defined(TEST_124)
 #include <experimental/forward_list>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_124 > /dev/null 2> %t/header.experimental_iterator
-#if defined(TEST_124)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_125 > /dev/null 2> %t/header.experimental_iterator
+#if defined(TEST_125)
 #include <experimental/iterator>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_125 > /dev/null 2> %t/header.experimental_list
-#if defined(TEST_125)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_126 > /dev/null 2> %t/header.experimental_list
+#if defined(TEST_126)
 #include <experimental/list>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_126 > /dev/null 2> %t/header.experimental_map
-#if defined(TEST_126)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_127 > /dev/null 2> %t/header.experimental_map
+#if defined(TEST_127)
 #include <experimental/map>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_127 > /dev/null 2> %t/header.experimental_memory_resource
-#if defined(TEST_127)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_128 > /dev/null 2> %t/header.experimental_memory_resource
+#if defined(TEST_128)
 #include <experimental/memory_resource>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_128 > /dev/null 2> %t/header.experimental_propagate_const
-#if defined(TEST_128)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_129 > /dev/null 2> %t/header.experimental_propagate_const
+#if defined(TEST_129)
 #include <experimental/propagate_const>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_129 > /dev/null 2> %t/header.experimental_regex
-#if defined(TEST_129)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_130 > /dev/null 2> %t/header.experimental_regex
+#if defined(TEST_130)
 #include <experimental/regex>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_130 > /dev/null 2> %t/header.experimental_set
-#if defined(TEST_130)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_131 > /dev/null 2> %t/header.experimental_set
+#if defined(TEST_131)
 #include <experimental/set>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_131 > /dev/null 2> %t/header.experimental_simd
-#if defined(TEST_131)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_132 > /dev/null 2> %t/header.experimental_simd
+#if defined(TEST_132)
 #include <experimental/simd>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_132 > /dev/null 2> %t/header.experimental_string
-#if defined(TEST_132)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_133 > /dev/null 2> %t/header.experimental_string
+#if defined(TEST_133)
 #include <experimental/string>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_133 > /dev/null 2> %t/header.experimental_type_traits
-#if defined(TEST_133)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_134 > /dev/null 2> %t/header.experimental_type_traits
+#if defined(TEST_134)
 #include <experimental/type_traits>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_134 > /dev/null 2> %t/header.experimental_unordered_map
-#if defined(TEST_134)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_135 > /dev/null 2> %t/header.experimental_unordered_map
+#if defined(TEST_135)
 #include <experimental/unordered_map>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_135 > /dev/null 2> %t/header.experimental_unordered_set
-#if defined(TEST_135)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_136 > /dev/null 2> %t/header.experimental_unordered_set
+#if defined(TEST_136)
 #include <experimental/unordered_set>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_136 > /dev/null 2> %t/header.experimental_utility
-#if defined(TEST_136)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_137 > /dev/null 2> %t/header.experimental_utility
+#if defined(TEST_137)
 #include <experimental/utility>
 #endif
-// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_137 > /dev/null 2> %t/header.experimental_vector
-#if defined(TEST_137)
+// RUN: %{cxx} %s %{flags} %{compile_flags} --trace-includes -fshow-skipped-includes --preprocess -DTEST_138 > /dev/null 2> %t/header.experimental_vector
+#if defined(TEST_138)
 #include <experimental/vector>
 #endif
 // RUN: %{python} %S/transitive_includes_to_csv.py %t > %t/transitive_includes.csv

diff  --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index 172f222d18cbc..0770eaaed8210 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -515,6 +515,12 @@ map tuple
 map type_traits
 map utility
 map version
+mdspan array
+mdspan cinttypes
+mdspan concepts
+mdspan cstddef
+mdspan limits
+mdspan span
 memory atomic
 memory compare
 memory concepts

diff  --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index f75a9987622e6..f60756138b552 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -515,6 +515,12 @@ map tuple
 map type_traits
 map utility
 map version
+mdspan array
+mdspan cinttypes
+mdspan concepts
+mdspan cstddef
+mdspan limits
+mdspan span
 memory atomic
 memory compare
 memory concepts

diff  --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index f402f3e2e66d7..2fe6248ae6670 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -517,6 +517,12 @@ map tuple
 map type_traits
 map utility
 map version
+mdspan array
+mdspan cinttypes
+mdspan concepts
+mdspan cstddef
+mdspan limits
+mdspan span
 memory atomic
 memory compare
 memory concepts

diff  --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index f402f3e2e66d7..2fe6248ae6670 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -517,6 +517,12 @@ map tuple
 map type_traits
 map utility
 map version
+mdspan array
+mdspan cinttypes
+mdspan concepts
+mdspan cstddef
+mdspan limits
+mdspan span
 memory atomic
 memory compare
 memory concepts

diff  --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv
index 79ad405068515..2c743b9672f21 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -523,6 +523,12 @@ map tuple
 map type_traits
 map utility
 map version
+mdspan array
+mdspan cinttypes
+mdspan concepts
+mdspan cstddef
+mdspan limits
+mdspan span
 memory atomic
 memory compare
 memory concepts

diff  --git a/libcxx/test/libcxx/transitive_includes/cxx2b.csv b/libcxx/test/libcxx/transitive_includes/cxx2b.csv
index 833df27c70f5a..0a386315e72a9 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx2b.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx2b.csv
@@ -351,6 +351,12 @@ map optional
 map stdexcept
 map tuple
 map version
+mdspan array
+mdspan cinttypes
+mdspan concepts
+mdspan cstddef
+mdspan limits
+mdspan span
 memory compare
 memory cstddef
 memory cstdint

diff  --git a/libcxx/test/std/containers/views/mdspan/extents/ConvertibleToIntegral.h b/libcxx/test/std/containers/views/mdspan/extents/ConvertibleToIntegral.h
new file mode 100644
index 0000000000000..1cdb198739ec9
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/extents/ConvertibleToIntegral.h
@@ -0,0 +1,22 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef TEST_STD_CONTAINERS_CONVERTIBLE_TO_INTEGRAL_H
+#define TEST_STD_CONTAINERS_CONVERTIBLE_TO_INTEGRAL_H
+
+struct IntType {
+  int val;
+  constexpr IntType() = default;
+  constexpr IntType(int v) noexcept : val(v){};
+
+  constexpr bool operator==(const IntType& rhs) const { return val == rhs.val; }
+  constexpr operator int() const noexcept { return val; }
+  constexpr operator unsigned char() const noexcept { return val; }
+};
+
+#endif // TEST_STD_CONTAINERS_CONVERTIBLE_TO_INTEGRAL_H

diff  --git a/libcxx/test/std/containers/views/mdspan/extents/CtorTestCombinations.h b/libcxx/test/std/containers/views/mdspan/extents/CtorTestCombinations.h
new file mode 100644
index 0000000000000..4914b5c1d8749
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/extents/CtorTestCombinations.h
@@ -0,0 +1,99 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+
+#include <mdspan>
+#include <cassert>
+#include <array>
+#include <span>
+
+#include "ConvertibleToIntegral.h"
+#include "test_macros.h"
+
+// Helper file to implement combinatorical testing of extents constructor
+//
+// std::extents can be constructed from just indices, a std::array, or a std::span
+// In each of those cases one can either provide all extents, or just the dynamic ones
+// If constructed from std::span, the span needs to have a static extent
+// Furthermore, the indices/array/span can have integer types other than index_type
+
+template <class E, class AllExtents>
+constexpr void test_runtime_observers(E ext, AllExtents expected) {
+  for (typename E::rank_type r = 0; r < ext.rank(); r++) {
+    ASSERT_SAME_TYPE(decltype(ext.extent(0)), typename E::index_type);
+    ASSERT_NOEXCEPT(ext.extent(0));
+    assert(ext.extent(r) == static_cast<typename E::index_type>(expected[r]));
+  }
+}
+
+template <class E, class AllExtents>
+constexpr void test_implicit_construction_call(E e, AllExtents all_ext) {
+  test_runtime_observers(e, all_ext);
+}
+
+template <class E, class Test, class AllExtents>
+constexpr void test_construction(AllExtents all_ext) {
+  // test construction from all extents
+  Test::template test_construction<E>(all_ext, all_ext, std::make_index_sequence<E::rank()>());
+
+  // test construction from just dynamic extents
+  // create an array of just the extents corresponding to dynamic values
+  std::array<typename AllExtents::value_type, E::rank_dynamic()> dyn_ext{0};
+  size_t dynamic_idx = 0;
+  for (size_t r = 0; r < E::rank(); r++) {
+    if (E::static_extent(r) == std::dynamic_extent) {
+      dyn_ext[dynamic_idx] = all_ext[r];
+      dynamic_idx++;
+    }
+  }
+  Test::template test_construction<E>(all_ext, dyn_ext, std::make_index_sequence<E::rank_dynamic()>());
+}
+
+template <class T, class TArg, class Test>
+constexpr void test() {
+  constexpr size_t D = std::dynamic_extent;
+
+  test_construction<std::extents<T>, Test>(std::array<TArg, 0>{});
+
+  test_construction<std::extents<T, 3>, Test>(std::array<TArg, 1>{3});
+  test_construction<std::extents<T, D>, Test>(std::array<TArg, 1>{3});
+
+  test_construction<std::extents<T, 3, 7>, Test>(std::array<TArg, 2>{3, 7});
+  test_construction<std::extents<T, 3, D>, Test>(std::array<TArg, 2>{3, 7});
+  test_construction<std::extents<T, D, 7>, Test>(std::array<TArg, 2>{3, 7});
+  test_construction<std::extents<T, D, D>, Test>(std::array<TArg, 2>{3, 7});
+
+  test_construction<std::extents<T, 3, 7, 9>, Test>(std::array<TArg, 3>{3, 7, 9});
+  test_construction<std::extents<T, 3, 7, D>, Test>(std::array<TArg, 3>{3, 7, 9});
+  test_construction<std::extents<T, 3, D, D>, Test>(std::array<TArg, 3>{3, 7, 9});
+  test_construction<std::extents<T, D, 7, D>, Test>(std::array<TArg, 3>{3, 7, 9});
+  test_construction<std::extents<T, D, D, D>, Test>(std::array<TArg, 3>{3, 7, 9});
+  test_construction<std::extents<T, 3, D, 9>, Test>(std::array<TArg, 3>{3, 7, 9});
+  test_construction<std::extents<T, D, D, 9>, Test>(std::array<TArg, 3>{3, 7, 9});
+  test_construction<std::extents<T, D, 7, 9>, Test>(std::array<TArg, 3>{3, 7, 9});
+
+  test_construction<std::extents<T, 1, 2, 3, 4, 5, 6, 7, 8, 9>, Test>(std::array<TArg, 9>{1, 2, 3, 4, 5, 6, 7, 8, 9});
+  test_construction<std::extents<T, D, 2, 3, D, 5, D, 7, D, 9>, Test>(std::array<TArg, 9>{1, 2, 3, 4, 5, 6, 7, 8, 9});
+  test_construction<std::extents<T, D, D, D, D, D, D, D, D, D>, Test>(std::array<TArg, 9>{1, 2, 3, 4, 5, 6, 7, 8, 9});
+}
+
+template <class Test>
+constexpr bool test_index_type_combo() {
+  test<int, int, Test>();
+  test<int, size_t, Test>();
+  test<unsigned, int, Test>();
+  test<char, size_t, Test>();
+  test<long long, unsigned, Test>();
+  test<size_t, int, Test>();
+  test<size_t, size_t, Test>();
+  test<int, IntType, Test>();
+  test<unsigned char, IntType, Test>();
+  return true;
+}

diff  --git a/libcxx/test/std/containers/views/mdspan/extents/assert.conversion.pass.cpp b/libcxx/test/std/containers/views/mdspan/extents/assert.conversion.pass.cpp
new file mode 100644
index 0000000000000..1ca4e2ec178ac
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/extents/assert.conversion.pass.cpp
@@ -0,0 +1,57 @@
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// XFAIL: availability-verbose_abort-missing
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_ASSERTIONS=1
+
+// <mdspan>
+
+// template<class OtherIndexType, size_t... OtherExtents>
+//     constexpr explicit(see below) extents(const extents<OtherIndexType, OtherExtents...>&) noexcept;
+//
+// Constraints:
+//   * sizeof...(OtherExtents) == rank() is true.
+//   * ((OtherExtents == dynamic_extent || Extents == dynamic_extent ||
+//       OtherExtents == Extents) && ...) is true.
+//
+// Preconditions:
+//   * other.extent(r) equals Er for each r for which Er is a static extent, and
+//   * either
+//      - sizeof...(OtherExtents) is zero, or
+//      - other.extent(r) is representable as a value of type index_type for
+//        every rank index r of other.
+//
+// Remarks: The expression inside explicit is equivalent to:
+//          (((Extents != dynamic_extent) && (OtherExtents == dynamic_extent)) || ... ) ||
+//          (numeric_limits<index_type>::max() < numeric_limits<OtherIndexType>::max())
+
+#include <mdspan>
+#include <cassert>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+  constexpr size_t D = std::dynamic_extent;
+  std::extents<int, D, D> arg{1000, 5};
+
+  // working case
+  {
+    [[maybe_unused]] std::extents<size_t, D, 5> e(arg); // should work
+  }
+  // mismatch of static extent
+  {
+    TEST_LIBCPP_ASSERT_FAILURE(([=] { std::extents<int, D, 3> e(arg); }()),
+                               "extents construction: mismatch of provided arguments with static extents.");
+  }
+  // value out of range
+  {
+    TEST_LIBCPP_ASSERT_FAILURE(([=] { std::extents<char, D, 5> e(arg); }()),
+                               "extents ctor: arguments must be representable as index_type and nonnegative");
+  }
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/views/mdspan/extents/assert.ctor_from_array.pass.cpp b/libcxx/test/std/containers/views/mdspan/extents/assert.ctor_from_array.pass.cpp
new file mode 100644
index 0000000000000..b5e74df97ca2a
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/extents/assert.ctor_from_array.pass.cpp
@@ -0,0 +1,69 @@
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// XFAIL: availability-verbose_abort-missing
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_ASSERTIONS=1
+
+// <mdspan>
+
+// Test construction from array:
+//
+// template<class OtherIndexType, size_t N>
+//     constexpr explicit(N != rank_dynamic()) extents(const array<OtherIndexType, N>& exts) noexcept;
+//
+// Constraints:
+//   * is_convertible_v<const OtherIndexType&, index_type> is true,
+//   * is_nothrow_constructible_v<index_type, const OtherIndexType&> is true, and
+//   * N == rank_dynamic() || N == rank() is true.
+//
+// Preconditions:
+//   * If N != rank_dynamic() is true, exts[r] equals Er for each r for which
+//     Er is a static extent, and
+//   * either
+//     - N is zero, or
+//     - exts[r] is nonnegative and is representable as a value of type index_type
+//       for every rank index r.
+//
+
+#include <mdspan>
+#include <cassert>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+  constexpr size_t D = std::dynamic_extent;
+  // working case
+  {
+    [[maybe_unused]] std::extents<int, D, 5> e1(std::array{1000, 5}); // should work
+  }
+  // mismatch of static extent
+  {
+    TEST_LIBCPP_ASSERT_FAILURE(
+        ([] {
+          std::extents<int, D, 5> e1(std::array{1000, 3});
+        }()),
+        "extents construction: mismatch of provided arguments with static extents.");
+  }
+  // value out of range
+  {
+    TEST_LIBCPP_ASSERT_FAILURE(
+        ([] {
+          std::extents<char, D, 5> e1(std::array{1000, 5});
+        }()),
+        "extents ctor: arguments must be representable as index_type and nonnegative");
+  }
+  // negative value
+  {
+    TEST_LIBCPP_ASSERT_FAILURE(
+        ([] {
+          std::extents<char, D, 5> e1(std::array{-1, 5});
+        }()),
+        "extents ctor: arguments must be representable as index_type and nonnegative");
+  }
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/views/mdspan/extents/assert.ctor_from_integral.pass.cpp b/libcxx/test/std/containers/views/mdspan/extents/assert.ctor_from_integral.pass.cpp
new file mode 100644
index 0000000000000..69f8436f5653e
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/extents/assert.ctor_from_integral.pass.cpp
@@ -0,0 +1,62 @@
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// XFAIL: availability-verbose_abort-missing
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_ASSERTIONS=1
+
+// <mdspan>
+
+// Test construction from integral:
+//
+// template<class ... OtherIndexTypes>
+//     constexpr explicit extents(OtherIndexTypes ... exts) noexcept;
+//
+// Let N be sizeof...(OtherIndexTypes), and let
+// exts_arr be array<index_type, N>{static_cast<index_type>(std::move(exts))...}.
+//
+// Constraints:
+//   * (is_convertible_v<OtherIndexTypes, index_type> && ...) is true,
+//   * (is_nothrow_constructible_v<index_type, OtherIndexType> && ...) is true, and
+//   * N == rank_dynamic() || N == rank() is true.
+//
+// Preconditions:
+//   * If N != rank_dynamic() is true, exts_arr[r] equals Er for each r for which
+//     Er is a static extent, and
+//   * either
+//     - sizeof...(exts) == 0 is true, or
+//     - each element of exts is nonnegative and is representable as a value of type index_type.
+//
+
+#include <mdspan>
+#include <cassert>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+  constexpr size_t D = std::dynamic_extent;
+  // working case
+  {
+    [[maybe_unused]] std::extents<int, D, 5> e1(1000, 5); // should work
+  }
+  // mismatch of static extent
+  {
+    TEST_LIBCPP_ASSERT_FAILURE(([] { std::extents<int, D, 5> e1(1000, 3); }()),
+                               "extents construction: mismatch of provided arguments with static extents.");
+  }
+  // value out of range
+  {
+    TEST_LIBCPP_ASSERT_FAILURE(([] { std::extents<char, D, 5> e1(1000, 5); }()),
+                               "extents ctor: arguments must be representable as index_type and nonnegative");
+  }
+  // negative value
+  {
+    TEST_LIBCPP_ASSERT_FAILURE(([] { std::extents<char, D, 5> e1(-1, 5); }()),
+                               "extents ctor: arguments must be representable as index_type and nonnegative");
+  }
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/views/mdspan/extents/assert.ctor_from_span.pass.cpp b/libcxx/test/std/containers/views/mdspan/extents/assert.ctor_from_span.pass.cpp
new file mode 100644
index 0000000000000..0375ca43054a6
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/extents/assert.ctor_from_span.pass.cpp
@@ -0,0 +1,62 @@
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// XFAIL: availability-verbose_abort-missing
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_ASSERTIONS=1
+
+// Test construction from span:
+//
+// template<class OtherIndexType, size_t N>
+//     constexpr explicit(N != rank_dynamic()) extents(span<OtherIndexType, N> exts) noexcept;
+//
+// Constraints:
+//   * is_convertible_v<const OtherIndexType&, index_type> is true,
+//   * is_nothrow_constructible_v<index_type, const OtherIndexType&> is true, and
+//   * N == rank_dynamic() || N == rank() is true.
+//
+// Preconditions:
+//   * If N != rank_dynamic() is true, exts[r] equals Er for each r for which
+//     Er is a static extent, and
+//   * either
+//     - N is zero, or
+//     - exts[r] is nonnegative and is representable as a value of type index_type
+//       for every rank index r.
+//
+
+#include <mdspan>
+#include <cassert>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+  constexpr size_t D = std::dynamic_extent;
+  // working case sanity check
+  {
+    std::array args{1000, 5};
+    [[maybe_unused]] std::extents<int, D, 5> e1(std::span{args});
+  }
+  // mismatch of static extent
+  {
+    std::array args{1000, 3};
+    TEST_LIBCPP_ASSERT_FAILURE(([=] { std::extents<int, D, 5> e1(std::span{args}); }()),
+                               "extents construction: mismatch of provided arguments with static extents.");
+  }
+  // value out of range
+  {
+    std::array args{1000, 5};
+    TEST_LIBCPP_ASSERT_FAILURE(([=] { std::extents<char, D, 5> e1(std::span{args}); }()),
+                               "extents ctor: arguments must be representable as index_type and nonnegative");
+  }
+  // negative value
+  {
+    std::array args{-1, 5};
+    TEST_LIBCPP_ASSERT_FAILURE(([=] { std::extents<char, D, 5> e1(std::span{args}); }()),
+                               "extents ctor: arguments must be representable as index_type and nonnegative");
+  }
+  return 0;
+}

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
new file mode 100644
index 0000000000000..acf09bdc0c5ac
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/extents/assert.obs.pass.cpp
@@ -0,0 +1,65 @@
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// XFAIL: availability-verbose_abort-missing
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_ASSERTIONS=1
+
+// <mdspan>
+
+// static constexpr size_t static_extent(rank_type i) noexcept;
+//
+//   Preconditions: i < rank() is true.
+//
+//   Returns: Ei.
+//
+//
+// constexpr index_type extent(rank_type i) const noexcept;
+//
+//   Preconditions: i < rank() is true.
+//
+//   Returns: Di.
+
+#include <mdspan>
+#include <cassert>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+  constexpr size_t D = std::dynamic_extent;
+
+  // mismatch of static extent
+  {
+    std::extents<int> e;
+    TEST_LIBCPP_ASSERT_FAILURE(([=] { e.extent(0); }()), "extents access: index must be less than rank");
+    TEST_LIBCPP_ASSERT_FAILURE(([=] { e.static_extent(0); }()), "extents access: index must be less than rank");
+  }
+  {
+    std::extents<int, D> e;
+    TEST_LIBCPP_ASSERT_FAILURE(([=] { e.extent(2); }()), "extents access: index must be less than rank");
+    TEST_LIBCPP_ASSERT_FAILURE(([=] { e.static_extent(2); }()), "extents access: index must be less than rank");
+  }
+  {
+    std::extents<int, 5> e;
+    TEST_LIBCPP_ASSERT_FAILURE(([=] { e.extent(2); }()), "extents access: index must be less than rank");
+    TEST_LIBCPP_ASSERT_FAILURE(([=] { e.static_extent(2); }()), "extents access: index must be less than rank");
+  }
+  {
+    std::extents<int, D, 5> e;
+    TEST_LIBCPP_ASSERT_FAILURE(([=] { e.extent(2); }()), "extents access: index must be less than rank");
+    TEST_LIBCPP_ASSERT_FAILURE(([=] { e.static_extent(2); }()), "extents access: index must be less than rank");
+  }
+  {
+    std::extents<int, 1, 2, 3, 4, 5, 6, 7, 8> e;
+    TEST_LIBCPP_ASSERT_FAILURE(([=] { e.extent(9); }()), "extents access: index must be less than rank");
+    TEST_LIBCPP_ASSERT_FAILURE(([=] { e.static_extent(9); }()), "extents access: index must be less than rank");
+  }
+
+  // check that static_extent works in constant expression with assertions enabled
+  static_assert(std::extents<int, D, 5>::static_extent(1) == 5);
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/views/mdspan/extents/comparison.pass.cpp b/libcxx/test/std/containers/views/mdspan/extents/comparison.pass.cpp
new file mode 100644
index 0000000000000..77fbd46fb7ca7
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/extents/comparison.pass.cpp
@@ -0,0 +1,95 @@
+//===----------------------------------------------------------------------===//
+//
+// 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... OtherExtents>
+//   friend constexpr bool operator==(const extents& lhs,
+//                                    const extents<OtherIndexType, OtherExtents...>& rhs) noexcept;
+//
+// Returns: true if lhs.rank() equals rhs.rank() and
+// if lhs.extent(r) equals rhs.extent(r) for every rank index r of rhs, otherwise false.
+//
+
+#include <mdspan>
+#include <type_traits>
+#include <concepts>
+#include <cassert>
+
+#include "test_macros.h"
+
+template <class To, class From>
+constexpr void test_comparison(bool equal, To dest, From src) {
+  ASSERT_NOEXCEPT(dest == src);
+  assert((dest == src) == equal);
+  assert((dest != src) == !equal);
+}
+
+template <class T1, class T2>
+constexpr void test_comparison_
diff erent_rank() {
+  constexpr size_t D = std::dynamic_extent;
+
+  test_comparison(false, std::extents<T1>(), std::extents<T2, D>(1));
+  test_comparison(false, std::extents<T1>(), std::extents<T2, 1>());
+
+  test_comparison(false, std::extents<T1, D>(1), std::extents<T2>());
+  test_comparison(false, std::extents<T1, 1>(), std::extents<T2>());
+
+  test_comparison(false, std::extents<T1, D>(5), std::extents<T2, D, D>(5, 5));
+  test_comparison(false, std::extents<T1, 5>(), std::extents<T2, 5, D>(5));
+  test_comparison(false, std::extents<T1, 5>(), std::extents<T2, 5, 1>());
+
+  test_comparison(false, std::extents<T1, D, D>(5, 5), std::extents<T2, D>(5));
+  test_comparison(false, std::extents<T1, 5, D>(5), std::extents<T2, D>(5));
+  test_comparison(false, std::extents<T1, 5, 5>(), std::extents<T2, 5>());
+}
+
+template <class T1, class T2>
+constexpr void test_comparison_same_rank() {
+  constexpr size_t D = std::dynamic_extent;
+
+  test_comparison(true, std::extents<T1>(), std::extents<T2>());
+
+  test_comparison(true, std::extents<T1, D>(5), std::extents<T2, D>(5));
+  test_comparison(true, std::extents<T1, 5>(), std::extents<T2, D>(5));
+  test_comparison(true, std::extents<T1, D>(5), std::extents<T2, 5>());
+  test_comparison(true, std::extents<T1, 5>(), std::extents< T2, 5>());
+  test_comparison(false, std::extents<T1, D>(5), std::extents<T2, D>(7));
+  test_comparison(false, std::extents<T1, 5>(), std::extents<T2, D>(7));
+  test_comparison(false, std::extents<T1, D>(5), std::extents<T2, 7>());
+  test_comparison(false, std::extents<T1, 5>(), std::extents<T2, 7>());
+
+  test_comparison(true, std::extents<T1, D, D, D, D, D>(5, 6, 7, 8, 9), std::extents<T2, D, D, D, D, D>(5, 6, 7, 8, 9));
+  test_comparison(true, std::extents<T1, D, 6, D, 8, D>(5, 7, 9), std::extents<T2, 5, D, D, 8, 9>(6, 7));
+  test_comparison(true, std::extents<T1, 5, 6, 7, 8, 9>(5, 6, 7, 8, 9), std::extents<T2, 5, 6, 7, 8, 9>());
+  test_comparison(
+      false, std::extents<T1, D, D, D, D, D>(5, 6, 7, 8, 9), std::extents<T2, D, D, D, D, D>(5, 6, 3, 8, 9));
+  test_comparison(false, std::extents<T1, D, 6, D, 8, D>(5, 7, 9), std::extents<T2, 5, D, D, 3, 9>(6, 7));
+  test_comparison(false, std::extents<T1, 5, 6, 7, 8, 9>(5, 6, 7, 8, 9), std::extents<T2, 5, 6, 7, 3, 9>());
+}
+
+template <class T1, class T2>
+constexpr void test_comparison() {
+  test_comparison_same_rank<T1, T2>();
+  test_comparison_
diff erent_rank<T1, T2>();
+}
+
+constexpr bool test() {
+  test_comparison<int, int>();
+  test_comparison<int, size_t>();
+  test_comparison<size_t, int>();
+  test_comparison<size_t, long>();
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/views/mdspan/extents/conversion.pass.cpp b/libcxx/test/std/containers/views/mdspan/extents/conversion.pass.cpp
new file mode 100644
index 0000000000000..6b0ecff02baab
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/extents/conversion.pass.cpp
@@ -0,0 +1,138 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <mdspan>
+
+// template<class OtherIndexType, size_t... OtherExtents>
+//     constexpr explicit(see below) extents(const extents<OtherIndexType, OtherExtents...>&) noexcept;
+//
+// Constraints:
+//   * sizeof...(OtherExtents) == rank() is true.
+//   * ((OtherExtents == dynamic_extent || Extents == dynamic_extent ||
+//       OtherExtents == Extents) && ...) is true.
+//
+// Preconditions:
+//   * other.extent(r) equals Er for each r for which Er is a static extent, and
+//   * either
+//      - sizeof...(OtherExtents) is zero, or
+//      - other.extent(r) is representable as a value of type index_type for
+//        every rank index r of other.
+//
+// Remarks: The expression inside explicit is equivalent to:
+//          (((Extents != dynamic_extent) && (OtherExtents == dynamic_extent)) || ... ) ||
+//          (numeric_limits<index_type>::max() < numeric_limits<OtherIndexType>::max())
+
+#include <mdspan>
+#include <type_traits>
+#include <concepts>
+#include <cassert>
+#include <limits>
+
+#include "test_macros.h"
+
+template <class To, class From>
+constexpr void test_implicit_conversion(To dest, From src) {
+  assert(dest == src);
+}
+
+template <bool implicit, class To, class From>
+constexpr void test_conversion(From src) {
+  To dest(src);
+  assert(dest == src);
+  if constexpr (implicit) {
+    dest = src;
+    assert(dest == src);
+    test_implicit_conversion<To, From>(src, src);
+  }
+}
+
+template <class T1, class T2>
+constexpr void test_conversion() {
+  constexpr size_t D = std::dynamic_extent;
+  constexpr bool idx_convertible =
+      static_cast<size_t>(std::numeric_limits<T1>::max()) >= static_cast<size_t>(std::numeric_limits<T2>::max());
+
+  // clang-format off
+  test_conversion<idx_convertible && true,  std::extents<T1>>(std::extents<T2>());
+  test_conversion<idx_convertible && true,  std::extents<T1, D>>(std::extents<T2, D>(5));
+  test_conversion<idx_convertible && false, std::extents<T1, 5>>(std::extents<T2, D>(5));
+  test_conversion<idx_convertible && true,  std::extents<T1, 5>>(std::extents<T2, 5>());
+  test_conversion<idx_convertible && false, std::extents<T1, 5, D>>(std::extents<T2, D, D>(5, 5));
+  test_conversion<idx_convertible && true,  std::extents<T1, D, D>>(std::extents<T2, D, D>(5, 5));
+  test_conversion<idx_convertible && true,  std::extents<T1, D, D>>(std::extents<T2, D, 7>(5));
+  test_conversion<idx_convertible && true,  std::extents<T1, 5, 7>>(std::extents<T2, 5, 7>());
+  test_conversion<idx_convertible && false, std::extents<T1, 5, D, 8, D, D>>(std::extents<T2, D, D, 8, 9, 1>(5, 7));
+  test_conversion<idx_convertible && true,  std::extents<T1, D, D, D, D, D>>(
+                                            std::extents<T2, D, D, D, D, D>(5, 7, 8, 9, 1));
+  test_conversion<idx_convertible && true,  std::extents<T1, D, D, 8, 9, D>>(std::extents<T2, D, 7, 8, 9, 1>(5));
+  test_conversion<idx_convertible && true,  std::extents<T1, 5, 7, 8, 9, 1>>(std::extents<T2, 5, 7, 8, 9, 1>());
+  // clang-format on
+}
+
+constexpr void test_no_implicit_conversion() {
+  constexpr size_t D = std::dynamic_extent;
+  // Sanity check that one static to dynamic conversion works
+  static_assert(std::is_constructible_v<std::extents<int, D>, std::extents<int, 5>>, "");
+  static_assert(std::is_convertible_v<std::extents<int, 5>, std::extents<int, D>>, "");
+
+  // Check that dynamic to static conversion only works explicitly only
+  static_assert(std::is_constructible_v<std::extents<int, 5>, std::extents<int, D>>, "");
+  static_assert(!std::is_convertible_v<std::extents<int, D>, std::extents<int, 5>>, "");
+
+  // Sanity check that one static to dynamic conversion works
+  static_assert(std::is_constructible_v<std::extents<int, D, 7>, std::extents<int, 5, 7>>, "");
+  static_assert(std::is_convertible_v<std::extents<int, 5, 7>, std::extents<int, D, 7>>, "");
+
+  // Check that dynamic to static conversion only works explicitly only
+  static_assert(std::is_constructible_v<std::extents<int, 5, 7>, std::extents<int, D, 7>>, "");
+  static_assert(!std::is_convertible_v<std::extents<int, D, 7>, std::extents<int, 5, 7>>, "");
+
+  // Sanity check that smaller index_type to larger index_type conversion works
+  static_assert(std::is_constructible_v<std::extents<size_t, 5>, std::extents<int, 5>>, "");
+  static_assert(std::is_convertible_v<std::extents<int, 5>, std::extents<size_t, 5>>, "");
+
+  // Check that larger index_type to smaller index_type conversion works explicitly only
+  static_assert(std::is_constructible_v<std::extents<int, 5>, std::extents<size_t, 5>>, "");
+  static_assert(!std::is_convertible_v<std::extents<size_t, 5>, std::extents<int, 5>>, "");
+}
+
+constexpr void test_rank_mismatch() {
+  constexpr size_t D = std::dynamic_extent;
+
+  static_assert(!std::is_constructible_v<std::extents<int, D>, std::extents<int>>, "");
+  static_assert(!std::is_constructible_v<std::extents<int>, std::extents<int, D, D>>, "");
+  static_assert(!std::is_constructible_v<std::extents<int, D>, std::extents<int, D, D>>, "");
+  static_assert(!std::is_constructible_v<std::extents<int, D, D, D>, std::extents<int, D, D>>, "");
+}
+
+constexpr void test_static_extent_mismatch() {
+  constexpr size_t D = std::dynamic_extent;
+
+  static_assert(!std::is_constructible_v<std::extents<int, D, 5>, std::extents<int, D, 4>>, "");
+  static_assert(!std::is_constructible_v<std::extents<int, 5>, std::extents<int, 4>>, "");
+  static_assert(!std::is_constructible_v<std::extents<int, 5, D>, std::extents<int, 4, D>>, "");
+}
+
+constexpr bool test() {
+  test_conversion<int, int>();
+  test_conversion<int, size_t>();
+  test_conversion<size_t, int>();
+  test_conversion<size_t, long>();
+  test_no_implicit_conversion();
+  test_rank_mismatch();
+  test_static_extent_mismatch();
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/views/mdspan/extents/ctad.pass.cpp b/libcxx/test/std/containers/views/mdspan/extents/ctad.pass.cpp
new file mode 100644
index 0000000000000..81d85c1faf0f7
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/extents/ctad.pass.cpp
@@ -0,0 +1,46 @@
+//===----------------------------------------------------------------------===//
+//
+// 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... Integrals>
+// explicit extents(Integrals...) -> see below;
+//   Constraints: (is_convertible_v<Integrals, size_t> && ...) is true.
+//
+// Remarks: The deduced type is dextents<size_t, sizeof...(Integrals)>.
+
+#include <mdspan>
+#include <cassert>
+
+#include "ConvertibleToIntegral.h"
+#include "test_macros.h"
+
+template <class E, class Expected>
+constexpr void test(E e, Expected expected) {
+  ASSERT_SAME_TYPE(E, Expected);
+  assert(e == expected);
+}
+
+constexpr bool test() {
+  constexpr std::size_t D = std::dynamic_extent;
+
+  test(std::extents(), std::extents<size_t>());
+  test(std::extents(1), std::extents<std::size_t, D>(1));
+  test(std::extents(1, 2u), std::extents<std::size_t, D, D>(1, 2u));
+  test(std::extents(1, 2u, 3, 4, 5, 6, 7, 8, 9),
+       std::extents<std::size_t, D, D, D, D, D, D, D, D, D>(1, 2u, 3, 4, 5, 6, 7, 8, 9));
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/views/mdspan/extents/ctor_default.pass.cpp b/libcxx/test/std/containers/views/mdspan/extents/ctor_default.pass.cpp
new file mode 100644
index 0000000000000..b2de473f8e8c6
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/extents/ctor_default.pass.cpp
@@ -0,0 +1,49 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <mdspan>
+
+// Test default construction:
+//
+// constexpr extents() noexcept = default;
+//
+// Remarks: since the standard uses an exposition only array member, dynamic extents
+// need to be zero intialized!
+
+#include <mdspan>
+#include <cassert>
+#include <array>
+
+#include "ConvertibleToIntegral.h"
+#include "CtorTestCombinations.h"
+#include "test_macros.h"
+
+struct DefaultCtorTest {
+  template <class E, class AllExtents, class Extents, size_t... Indices>
+  static constexpr void test_construction(AllExtents all_ext, Extents, std::index_sequence<Indices...>) {
+    // This function gets called twice: once with Extents being just the dynamic ones, and once with all the extents specified.
+    // We only test during the all extent case, since then Indices is the correct number. This allows us to reuse the same
+    // testing machinery used in other constructor tests.
+    if constexpr (sizeof...(Indices) == E::rank()) {
+      ASSERT_NOEXCEPT(E{});
+      // Need to construct new expected values, replacing dynamic values with 0
+      std::array<typename AllExtents::value_type, E::rank()> expected_exts{
+          ((E::static_extent(Indices) == std::dynamic_extent)
+               ? typename AllExtents::value_type(0)
+               : all_ext[Indices])...};
+      test_runtime_observers(E{}, expected_exts);
+    }
+  }
+};
+
+int main(int, char**) {
+  test_index_type_combo<DefaultCtorTest>();
+  static_assert(test_index_type_combo<DefaultCtorTest>());
+  return 0;
+}

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
new file mode 100644
index 0000000000000..48286e1128498
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/extents/ctor_from_array.pass.cpp
@@ -0,0 +1,86 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 construction from array:
+//
+// template<class OtherIndexType, size_t N>
+//     constexpr explicit(N != rank_dynamic()) extents(const array<OtherIndexType, N>& exts) noexcept;
+//
+// Constraints:
+//   * is_convertible_v<const OtherIndexType&, index_type> is true,
+//   * is_nothrow_constructible_v<index_type, const OtherIndexType&> is true, and
+//   * N == rank_dynamic() || N == rank() is true.
+//
+// Preconditions:
+//   * If N != rank_dynamic() is true, exts[r] equals Er for each r for which
+//     Er is a static extent, and
+//   * either
+//     - N is zero, or
+//     - exts[r] is nonnegative and is representable as a value of type index_type
+//       for every rank index r.
+//
+
+#include <mdspan>
+#include <cassert>
+#include <array>
+#include <type_traits>
+
+#include "ConvertibleToIntegral.h"
+#include "CtorTestCombinations.h"
+#include "test_macros.h"
+
+struct ArrayCtorTest {
+  template <class E, class T, size_t N, class Extents, size_t... Indices>
+  static constexpr void test_construction(std::array<T, N> all_ext, Extents ext, std::index_sequence<Indices...>) {
+    ASSERT_NOEXCEPT(E(ext));
+    if constexpr (N == E::rank_dynamic()) {
+      test_implicit_construction_call<E>(ext, all_ext);
+    }
+    test_runtime_observers(E(ext), all_ext);
+  }
+};
+
+template <class E>
+struct implicit_construction {
+  bool value;
+  implicit_construction(E) : value(true) {}
+  template <class T>
+  implicit_construction(T) : value(false) {}
+};
+
+int main(int, char**) {
+  test_index_type_combo<ArrayCtorTest>();
+  static_assert(test_index_type_combo<ArrayCtorTest>());
+
+  constexpr size_t D = std::dynamic_extent;
+  using E            = std::extents<int, 1, D, 3, D>;
+
+  // check can't construct from too few arguments
+  static_assert(!std::is_constructible_v<E, std::array<int, 1>>, "extents constructible from illegal arguments");
+  // check can't construct from rank_dynamic < #args < rank
+  static_assert(!std::is_constructible_v<E, std::array<int, 3>>, "extents constructible from illegal arguments");
+  // check can't construct from too many arguments
+  static_assert(!std::is_constructible_v<E, std::array<int, 5>>, "extents constructible from illegal arguments");
+
+  // test implicit construction fails from span and array if all extents are given
+  std::array a5{3, 4, 5, 6, 7};
+  // check that explicit construction works, i.e. no error
+  static_assert(std::is_constructible_v< std::extents<int, D, D, 5, D, D>, decltype(a5)>,
+                "extents unexpectectly not constructible");
+  // check that implicit construction doesn't work
+  assert((implicit_construction<std::extents<int, D, D, 5, D, D>>(a5).value == false));
+
+  // test construction fails from types not convertible to index_type but convertible to other integer types
+  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");
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/views/mdspan/extents/ctor_from_integral.pass.cpp b/libcxx/test/std/containers/views/mdspan/extents/ctor_from_integral.pass.cpp
new file mode 100644
index 0000000000000..fd3624e74a3d9
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/extents/ctor_from_integral.pass.cpp
@@ -0,0 +1,69 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 construction from integral:
+//
+// template<class ... OtherIndexTypes>
+//     constexpr explicit extents(OtherIndexTypes ... exts) noexcept;
+//
+// Let N be sizeof...(OtherIndexTypes), and let
+// exts_arr be array<index_type, N>{static_cast<index_type>(std::move(exts))...}.
+//
+// Constraints:
+//   * (is_convertible_v<OtherIndexTypes, index_type> && ...) is true,
+//   * (is_nothrow_constructible_v<index_type, OtherIndexType> && ...) is true, and
+//   * N == rank_dynamic() || N == rank() is true.
+//
+// Preconditions:
+//   * If N != rank_dynamic() is true, exts_arr[r] equals Er for each r for which
+//     Er is a static extent, and
+//   * either
+//     - sizeof...(exts) == 0 is true, or
+//     - each element of exts is nonnegative and is representable as a value of type index_type.
+//
+
+#include <mdspan>
+#include <cassert>
+#include <type_traits>
+
+#include "ConvertibleToIntegral.h"
+#include "CtorTestCombinations.h"
+#include "test_macros.h"
+
+struct IntegralCtorTest {
+  template <class E, class AllExtents, class Extents, size_t... Indices>
+  static constexpr void test_construction(AllExtents all_ext, Extents ext, std::index_sequence<Indices...>) {
+    // construction from indices
+    ASSERT_NOEXCEPT(E(ext[Indices]...));
+    test_runtime_observers(E(ext[Indices]...), all_ext);
+  }
+};
+
+int main(int, char**) {
+  test_index_type_combo<IntegralCtorTest>();
+  static_assert(test_index_type_combo<IntegralCtorTest>());
+
+  constexpr size_t D = std::dynamic_extent;
+  using E            = std::extents<int, 1, D, 3, D>;
+
+  // check can't construct from too few arguments
+  static_assert(!std::is_constructible_v<E, int>, "extents constructible from illegal arguments");
+  // check can't construct from rank_dynamic < #args < rank
+  static_assert(!std::is_constructible_v<E, int, int, int>, "extents constructible from illegal arguments");
+  // check can't construct from too many arguments
+  static_assert(!std::is_constructible_v<E, int, int, int, int, int>, "extents constructible from illegal arguments");
+
+  // test construction fails from types not convertible to index_type but convertible to other integer types
+  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>, IntType>,
+                "extents constructible from illegal arguments");
+  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
new file mode 100644
index 0000000000000..2e69bab1461ca
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/extents/ctor_from_span.pass.cpp
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <mdspan>
+
+// Test construction from span:
+//
+// template<class OtherIndexType, size_t N>
+//     constexpr explicit(N != rank_dynamic()) extents(span<OtherIndexType, N> exts) noexcept;
+//
+// Constraints:
+//   * is_convertible_v<const OtherIndexType&, index_type> is true,
+//   * is_nothrow_constructible_v<index_type, const OtherIndexType&> is true, and
+//   * N == rank_dynamic() || N == rank() is true.
+//
+// Preconditions:
+//   * If N != rank_dynamic() is true, exts[r] equals Er for each r for which
+//     Er is a static extent, and
+//   * either
+//     - N is zero, or
+//     - exts[r] is nonnegative and is representable as a value of type index_type
+//       for every rank index r.
+//
+
+#include <mdspan>
+#include <cassert>
+#include <array>
+#include <span>
+#include <type_traits>
+
+#include "ConvertibleToIntegral.h"
+#include "CtorTestCombinations.h"
+#include "test_macros.h"
+
+struct SpanCtorTest {
+  template <class E, class T, size_t N, class Extents, size_t... Indices>
+  static constexpr void test_construction(std::array<T, N> all_ext, Extents ext, std::index_sequence<Indices...>) {
+    ASSERT_NOEXCEPT(E(ext));
+    if constexpr (N == E::rank_dynamic()) {
+      test_implicit_construction_call<E>(std::span(ext), all_ext);
+    }
+    test_runtime_observers(E(std::span(ext)), all_ext);
+  }
+};
+
+template <class E>
+struct implicit_construction {
+  bool value;
+  implicit_construction(E) : value(true) {}
+  template <class T>
+  implicit_construction(T) : value(false) {}
+};
+
+int main(int, char**) {
+  test_index_type_combo<SpanCtorTest>();
+  static_assert(test_index_type_combo<SpanCtorTest>());
+
+  constexpr size_t D = std::dynamic_extent;
+  using E            = std::extents<int, 1, D, 3, D>;
+
+  // check can't construct from too few arguments
+  static_assert(!std::is_constructible_v<E, std::span<int, 1>>, "extents constructible from illegal arguments");
+  // check can't construct from rank_dynamic < #args < rank
+  static_assert(!std::is_constructible_v<E, std::span<int, 3>>, "extents constructible from illegal arguments");
+  // check can't construct from too many arguments
+  static_assert(!std::is_constructible_v<E, std::span<int, 5>>, "extents constructible from illegal arguments");
+
+  // test implicit construction fails from span and array if all extents are given
+  std::array a5{3, 4, 5, 6, 7};
+  std::span<int, 5> s5(a5.data(), 5);
+  // check that explicit construction works, i.e. no error
+  static_assert(std::is_constructible_v< std::extents<int, D, D, 5, D, D>, decltype(s5)>,
+                "extents unexpectectly not constructible");
+  // check that implicit construction doesn't work
+  assert((implicit_construction<std::extents<int, D, D, 5, D, D>>(s5).value == false));
+
+  // test construction fails from types not convertible to index_type but convertible to other integer types
+  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");
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/views/mdspan/extents/dextents.pass.cpp b/libcxx/test/std/containers/views/mdspan/extents/dextents.pass.cpp
new file mode 100644
index 0000000000000..a9fc8f3bed074
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/extents/dextents.pass.cpp
@@ -0,0 +1,39 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <mdspan>
+
+// template<class IndexType, size_t Rank>
+//     using dextents = see below;
+//
+// Result: A type E that is a specialization of extents such that
+//         E::rank() == Rank && E::rank() == E::rank_dynamic() is true,
+//         and E::index_type denotes IndexType.
+
+#include <mdspan>
+#include <cstddef>
+
+#include "test_macros.h"
+
+template <class IndexType>
+void test_alias_template_dextents() {
+  constexpr size_t D = std::dynamic_extent;
+  ASSERT_SAME_TYPE(std::dextents<IndexType, 0>, std::extents<IndexType>);
+  ASSERT_SAME_TYPE(std::dextents<IndexType, 1>, std::extents<IndexType, D>);
+  ASSERT_SAME_TYPE(std::dextents<IndexType, 2>, std::extents<IndexType, D, D>);
+  ASSERT_SAME_TYPE(std::dextents<IndexType, 3>, std::extents<IndexType, D, D, D>);
+  ASSERT_SAME_TYPE(std::dextents<IndexType, 9>, std::extents<IndexType, D, D, D, D, D, D, D, D, D>);
+}
+
+int main(int, char**) {
+  test_alias_template_dextents<int>();
+  test_alias_template_dextents<unsigned int>();
+  test_alias_template_dextents<size_t>();
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/views/mdspan/extents/obs_static.pass.cpp b/libcxx/test/std/containers/views/mdspan/extents/obs_static.pass.cpp
new file mode 100644
index 0000000000000..90b482b3bc065
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/extents/obs_static.pass.cpp
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <mdspan>
+
+// static constexpr rank_type rank() noexcept;
+// static constexpr rank_type rank_dynamic() noexcept;
+//
+// static constexpr size_t static_extent(rank_type i) noexcept;
+//
+//   Preconditions: i < rank() is true.
+//
+//   Returns: Ei.
+//
+//
+// constexpr index_type extent(rank_type i) const noexcept;
+//
+//   Preconditions: i < rank() is true.
+//
+//   Returns: Di.
+//
+
+#include <mdspan>
+#include <cassert>
+#include <utility>
+
+#include "test_macros.h"
+
+template <class E, size_t rank, size_t rank_dynamic, size_t... StaticExts, size_t... Indices>
+void test_static_observers(std::index_sequence<StaticExts...>, std::index_sequence<Indices...>) {
+  ASSERT_NOEXCEPT(E::rank());
+  static_assert(E::rank() == rank);
+  ASSERT_NOEXCEPT(E::rank_dynamic());
+  static_assert(E::rank_dynamic() == rank_dynamic);
+
+  // Let's only test this if the call isn't a precondition violation
+  if constexpr (rank > 0) {
+    ASSERT_NOEXCEPT(E::static_extent(0));
+    ASSERT_SAME_TYPE(decltype(E::static_extent(0)), size_t);
+    static_assert(((E::static_extent(Indices) == StaticExts) && ...));
+  }
+}
+
+template <class E, size_t rank, size_t rank_dynamic, size_t... StaticExts>
+void test_static_observers() {
+  test_static_observers<E, rank, rank_dynamic>(
+      std::index_sequence<StaticExts...>(), std::make_index_sequence<sizeof...(StaticExts)>());
+}
+
+template <class T>
+void test() {
+  constexpr size_t D = std::dynamic_extent;
+  constexpr size_t S = 5;
+
+  test_static_observers<std::extents<T>, 0, 0>();
+
+  test_static_observers<std::extents<T, S>, 1, 0, S>();
+  test_static_observers<std::extents<T, D>, 1, 1, D>();
+
+  test_static_observers<std::extents<T, S, S>, 2, 0, S, S>();
+  test_static_observers<std::extents<T, S, D>, 2, 1, S, D>();
+  test_static_observers<std::extents<T, D, S>, 2, 1, D, S>();
+  test_static_observers<std::extents<T, D, D>, 2, 2, D, D>();
+
+  test_static_observers<std::extents<T, S, S, S>, 3, 0, S, S, S>();
+  test_static_observers<std::extents<T, S, S, D>, 3, 1, S, S, D>();
+  test_static_observers<std::extents<T, S, D, S>, 3, 1, S, D, S>();
+  test_static_observers<std::extents<T, D, S, S>, 3, 1, D, S, S>();
+  test_static_observers<std::extents<T, S, D, D>, 3, 2, S, D, D>();
+  test_static_observers<std::extents<T, D, S, D>, 3, 2, D, S, D>();
+  test_static_observers<std::extents<T, D, D, S>, 3, 2, D, D, S>();
+  test_static_observers<std::extents<T, D, D, D>, 3, 3, D, D, D>();
+}
+
+int main(int, char**) {
+  test<int>();
+  test<unsigned>();
+  test<char>();
+  test<long long>();
+  test<size_t>();
+  return 0;
+}

diff  --git a/libcxx/test/std/containers/views/mdspan/extents/types.pass.cpp b/libcxx/test/std/containers/views/mdspan/extents/types.pass.cpp
new file mode 100644
index 0000000000000..2281945333999
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/extents/types.pass.cpp
@@ -0,0 +1,85 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 IndexType, size_t... Extents>
+// class extents {
+// public:
+//  // types
+//  using index_type = IndexType;
+//  using size_type = make_unsigned_t<index_type>;
+//  using rank_type = size_t;
+//
+//  static constexpr rank_type rank() noexcept { return sizeof...(Extents); }
+//  static constexpr rank_type rank_dynamic() noexcept { return dynamic-index(rank()); }
+//  ...
+//  }
+
+#include <mdspan>
+#include <type_traits>
+#include <concepts>
+#include <cassert>
+
+#include "test_macros.h"
+
+template <class E, class IndexType, size_t... Extents>
+void testExtents() {
+  ASSERT_SAME_TYPE(typename E::index_type, IndexType);
+  ASSERT_SAME_TYPE(typename E::size_type, std::make_unsigned_t<IndexType>);
+  ASSERT_SAME_TYPE(typename E::rank_type, size_t);
+
+  static_assert(sizeof...(Extents) == E::rank());
+  static_assert((static_cast<size_t>(Extents == std::dynamic_extent) + ...) == E::rank_dynamic());
+
+  static_assert(std::regular<E>);
+  static_assert(std::is_trivially_copyable_v<E>);
+
+// Did never find a way to make this true on windows
+#ifndef _WIN32
+  LIBCPP_STATIC_ASSERT(std::is_empty_v<E> == (E::rank_dynamic() == 0));
+#endif
+}
+
+template <class IndexType, size_t... Extents>
+void testExtents() {
+  testExtents<std::extents<IndexType, Extents...>, IndexType, Extents...>();
+}
+
+template <class T>
+void test() {
+  constexpr size_t D = std::dynamic_extent;
+  testExtents<T, D>();
+  testExtents<T, 3>();
+  testExtents<T, 3, 3>();
+  testExtents<T, 3, D>();
+  testExtents<T, D, 3>();
+  testExtents<T, D, D>();
+  testExtents<T, 3, 3, 3>();
+  testExtents<T, 3, 3, D>();
+  testExtents<T, 3, D, D>();
+  testExtents<T, D, 3, D>();
+  testExtents<T, D, D, D>();
+  testExtents<T, 3, D, 3>();
+  testExtents<T, D, 3, 3>();
+  testExtents<T, D, D, 3>();
+
+  testExtents<T, 9, 8, 7, 6, 5, 4, 3, 2, 1>();
+  testExtents<T, 9, D, 7, 6, D, D, 3, D, D>();
+  testExtents<T, D, D, D, D, D, D, D, D, D>();
+}
+
+int main(int, char**) {
+  test<int>();
+  test<unsigned>();
+  test<char>();
+  test<long long>();
+  test<size_t>();
+  return 0;
+}

diff  --git a/libcxx/test/std/language.support/support.limits/support.limits.general/mdspan.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/mdspan.version.compile.pass.cpp
new file mode 100644
index 0000000000000..26c2600f14e6b
--- /dev/null
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/mdspan.version.compile.pass.cpp
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// WARNING: This test was generated by generate_feature_test_macro_components.py
+// and should not be edited manually.
+//
+// clang-format off
+
+// <mdspan>
+
+// Test the feature test macros defined by <mdspan>
+
+/*  Constant            Value
+    __cpp_lib_mdspan    202207L [C++2b]
+*/
+
+#include <mdspan>
+#include "test_macros.h"
+
+#if TEST_STD_VER < 14
+
+# ifdef __cpp_lib_mdspan
+#   error "__cpp_lib_mdspan should not be defined before c++2b"
+# endif
+
+#elif TEST_STD_VER == 14
+
+# ifdef __cpp_lib_mdspan
+#   error "__cpp_lib_mdspan should not be defined before c++2b"
+# endif
+
+#elif TEST_STD_VER == 17
+
+# ifdef __cpp_lib_mdspan
+#   error "__cpp_lib_mdspan should not be defined before c++2b"
+# endif
+
+#elif TEST_STD_VER == 20
+
+# ifdef __cpp_lib_mdspan
+#   error "__cpp_lib_mdspan should not be defined before c++2b"
+# endif
+
+#elif TEST_STD_VER > 20
+
+# if !defined(_LIBCPP_VERSION)
+#   ifndef __cpp_lib_mdspan
+#     error "__cpp_lib_mdspan should be defined in c++2b"
+#   endif
+#   if __cpp_lib_mdspan != 202207L
+#     error "__cpp_lib_mdspan should have the value 202207L in c++2b"
+#   endif
+# else // _LIBCPP_VERSION
+#   ifdef __cpp_lib_mdspan
+#     error "__cpp_lib_mdspan should not be defined because it is unimplemented in libc++!"
+#   endif
+# endif
+
+#endif // TEST_STD_VER > 20
+

diff  --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index a289eebb43093..e1bdde4551a78 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -117,6 +117,7 @@
     __cpp_lib_map_try_emplace                      201411L [C++17]
     __cpp_lib_math_constants                       201907L [C++20]
     __cpp_lib_math_special_functions               201603L [C++17]
+    __cpp_lib_mdspan                               202207L [C++2b]
     __cpp_lib_memory_resource                      201603L [C++17]
     __cpp_lib_move_iterator_concept                202207L [C++20]
     __cpp_lib_move_only_function                   202110L [C++2b]
@@ -585,6 +586,10 @@
 #   error "__cpp_lib_math_special_functions should not be defined before c++17"
 # endif
 
+# ifdef __cpp_lib_mdspan
+#   error "__cpp_lib_mdspan should not be defined before c++2b"
+# endif
+
 # ifdef __cpp_lib_memory_resource
 #   error "__cpp_lib_memory_resource should not be defined before c++17"
 # endif
@@ -1269,6 +1274,10 @@
 #   error "__cpp_lib_math_special_functions should not be defined before c++17"
 # endif
 
+# ifdef __cpp_lib_mdspan
+#   error "__cpp_lib_mdspan should not be defined before c++2b"
+# endif
+
 # ifdef __cpp_lib_memory_resource
 #   error "__cpp_lib_memory_resource should not be defined before c++17"
 # endif
@@ -2100,6 +2109,10 @@
 #   endif
 # endif
 
+# ifdef __cpp_lib_mdspan
+#   error "__cpp_lib_mdspan should not be defined before c++2b"
+# endif
+
 # ifndef __cpp_lib_memory_resource
 #   error "__cpp_lib_memory_resource should be defined in c++17"
 # endif
@@ -3216,6 +3229,10 @@
 #   endif
 # endif
 
+# ifdef __cpp_lib_mdspan
+#   error "__cpp_lib_mdspan should not be defined before c++2b"
+# endif
+
 # ifndef __cpp_lib_memory_resource
 #   error "__cpp_lib_memory_resource should be defined in c++20"
 # endif
@@ -4488,6 +4505,19 @@
 #   endif
 # endif
 
+# if !defined(_LIBCPP_VERSION)
+#   ifndef __cpp_lib_mdspan
+#     error "__cpp_lib_mdspan should be defined in c++2b"
+#   endif
+#   if __cpp_lib_mdspan != 202207L
+#     error "__cpp_lib_mdspan should have the value 202207L in c++2b"
+#   endif
+# else // _LIBCPP_VERSION
+#   ifdef __cpp_lib_mdspan
+#     error "__cpp_lib_mdspan should not be defined because it is unimplemented in libc++!"
+#   endif
+# endif
+
 # ifndef __cpp_lib_memory_resource
 #   error "__cpp_lib_memory_resource should be defined in c++2b"
 # endif

diff  --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index fb59395115e35..5ace82348dcb7 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -494,6 +494,11 @@ def add_version_header(tc):
     "values": { "c++17": 201603 },
     "headers": ["cmath"],
     "unimplemented": True,
+  }, {
+    "name": "__cpp_lib_mdspan",
+    "values": { "c++2b": 202207 },
+    "headers": ["mdspan"],
+    "unimplemented": True,
   }, {
     "name": "__cpp_lib_memory_resource",
     "values": { "c++17": 201603 },


        


More information about the libcxx-commits mailing list