+ }
+
+ constexpr const extents_type&
+ extents() const noexcept { return _M_mapping.extents(); }
+
+ constexpr const data_handle_type&
+ data_handle() const noexcept { return _M_handle; }
+
+ constexpr const mapping_type&
+ mapping() const noexcept { return _M_mapping; }
+
+ constexpr const accessor_type&
+ accessor() const noexcept { return _M_accessor; }
+
+ static constexpr bool
+ is_always_unique() { return mapping_type::is_always_unique(); }
+
+ static constexpr bool
+ is_always_exhaustive() { return mapping_type::is_always_exhaustive(); }
+
+ static constexpr bool
+ is_always_strided() { return mapping_type::is_always_strided(); }
+
+ constexpr bool
+ is_unique() const { return _M_mapping.is_unique(); }
+
+ constexpr bool
+ is_exhaustive() const { return _M_mapping.is_exhaustive(); }
+
+ constexpr bool
+ is_strided() const { return _M_mapping. is_strided(); }
+
+ constexpr index_type
+ stride(rank_type __r) const { return _M_mapping.stride(__r); }
+
+ private:
+ [[no_unique_address]] accessor_type _M_accessor;
+ [[no_unique_address]] mapping_type _M_mapping;
+ [[no_unique_address]] data_handle_type _M_handle;
+ };
+
+ template<typename _CArray>
+ requires is_array_v<_CArray> && (rank_v<_CArray> == 1)
+ mdspan(_CArray&)
+ -> mdspan<remove_all_extents_t<_CArray>,
+ extents<size_t, extent_v<_CArray, 0>>>;
+
+ template<typename _Pointer>
+ requires is_pointer_v<remove_reference_t<_Pointer>>
+ mdspan(_Pointer&&)
+ -> mdspan<remove_pointer_t<remove_reference_t<_Pointer>>, extents<size_t>>;
+
+ template<typename _ElementType, typename... _Integrals>
+ requires (is_convertible_v<_Integrals, size_t> && ...)
+ && (sizeof...(_Integrals) > 0)
+ explicit mdspan(_ElementType*, _Integrals...)
+ -> mdspan<_ElementType,
+ extents<size_t, __mdspan::__dynamic_extent<_Integrals>()...>>;
+
+ template<typename _ElementType, typename _OIndexType, size_t _Nm>
+ mdspan(_ElementType*, span<_OIndexType, _Nm>)
+ -> mdspan<_ElementType, dextents<size_t, _Nm>>;
+
+ template<typename _ElementType, typename _OIndexType, size_t _Nm>
+ mdspan(_ElementType*, const array<_OIndexType, _Nm>&)
+ -> mdspan<_ElementType, dextents<size_t, _Nm>>;
+
+ template<typename _ElementType, typename _IndexType, size_t... _ExtentsPack>
+ mdspan(_ElementType*, const extents<_IndexType, _ExtentsPack...>&)
+ -> mdspan<_ElementType, extents<_IndexType, _ExtentsPack...>>;
+
+ template<typename _ElementType, typename _MappingType>
+ mdspan(_ElementType*, const _MappingType&)
+ -> mdspan<_ElementType, typename _MappingType::extents_type,
+ typename _MappingType::layout_type>;
+
+ template<typename _MappingType, typename _AccessorType>
+ mdspan(const typename _AccessorType::data_handle_type&, const
_MappingType&,
+ const _AccessorType&)
+ -> mdspan<typename _AccessorType::element_type,
+ typename _MappingType::extents_type,
+ typename _MappingType::layout_type, _AccessorType>;
+
_GLIBCXX_END_NAMESPACE_VERSION
}
#endif
diff --git a/libstdc++-v3/src/c++23/std.cc.in b/libstdc++-v3/src/c++23/std.cc.in
index e692caaa5f9..b62114be467 100644
--- a/libstdc++-v3/src/c++23/std.cc.in
+++ b/libstdc++-v3/src/c++23/std.cc.in
@@ -1851,7 +1851,8 @@ export namespace std
using std::layout_right;
using std::layout_stride;
using std::default_accessor;
- // FIXME layout_left_padded, layout_right_padded, aligned_accessor and mdspan
+ using std::mdspan;
+ // FIXME layout_left_padded, layout_right_padded, aligned_accessor, mdsubspan
}
#endif
diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/class_mandate_neg.cc
b/libstdc++-v3/testsuite/23_containers/mdspan/class_mandate_neg.cc
new file mode 100644
index 00000000000..de19b6d85c9
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/class_mandate_neg.cc
@@ -0,0 +1,41 @@
+// { dg-do compile { target c++23 } }
+#include<mdspan>
+
+#include <cstdint>
+#include "layout_like.h"
+
+struct ExtentsLike
+{
+ using index_type = int;
+ using size_type = unsigned int;
+ using rank_type = size_t;
+
+ static constexpr size_t rank() { return 1; }
+ static constexpr size_t rank_dynamic() { return 0; }
+};
+
+constexpr bool
+test_custom_extents_type()
+{
+ std::mdspan<double, ExtentsLike> md1; // { dg-error "required from here" }
+ return true;
+}
+static_assert(test_custom_extents_type());
+
+constexpr bool
+test_element_type_mismatch()
+{
+ using E = std::extents<int, 1>;
+ using L = std::layout_right;
+ using A = std::default_accessor<double>;
+
+ [[maybe_unused]] std::mdspan<float, E, L, A> md2; // { dg-error "required from
here" }
+ return true;
+};
+static_assert(test_element_type_mismatch());
+
+// { dg-prune-output "Extents must be a specialization of std::extents" }
+// { dg-prune-output "no type named '_S_storage'" }
+// { dg-prune-output "non-constant condition" }
+// { dg-prune-output "static assertion failed" }
+// { dg-prune-output "__glibcxx_assert" }
diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layout_like.h
b/libstdc++-v3/testsuite/23_containers/mdspan/layout_like.h
new file mode 100644
index 00000000000..fc239d65411
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/layout_like.h
@@ -0,0 +1,80 @@
+#pragma once
+
+struct LayoutLike
+{
+ template<typename Extents>
+ class mapping
+ {
+ public:
+ using extents_type = Extents;
+ using index_type = typename extents_type::index_type;
+ using size_type = typename extents_type::size_type;
+ using rank_type = typename extents_type::rank_type;
+ using layout_type = LayoutLike;
+
+ constexpr
+ mapping() noexcept = default;
+
+ constexpr
+ mapping(Extents exts)
+ : m_exts(exts)
+ { }
+
+ constexpr const extents_type&
+ extents() const noexcept { return m_exts; }
+
+ constexpr index_type
+ required_span_size() const noexcept
+ {
+ for (size_t i = 0; i < extents_type::rank(); ++i)
+ if (m_exts.extent(i) == 0)
+ return 0;
+ return 1;
+ }
+
+ template<typename... Indices>
+ requires (sizeof...(Indices) == extents_type::rank())
+ constexpr index_type
+ operator()(Indices...) const noexcept
+ { return 0; }
+
+ static constexpr index_type
+ stride(rank_type) noexcept
+ { return 0; }
+
+ static constexpr bool
+ is_always_unique() noexcept
+ { return false; }
+
+ static constexpr bool
+ is_always_exhaustive() noexcept
+ { return true; }
+
+ static constexpr bool
+ is_always_strided() noexcept
+ { return true; }
+
+ constexpr bool
+ is_unique() noexcept
+ {
+ if (required_span_size() == 0)
+ return true;
+
+ for (size_t i = 0; i < extents_type::rank(); ++i)
+ if (m_exts.extent(i) > 1)
+ return false;
+ return true;
+ }
+
+ static constexpr bool
+ is_exhaustive() noexcept
+ { return true; }
+
+ static constexpr bool
+ is_strided() noexcept
+ { return true; }
+
+ private:
+ Extents m_exts;
+ };
+};
diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/mdspan.cc
b/libstdc++-v3/testsuite/23_containers/mdspan/mdspan.cc
new file mode 100644
index 00000000000..ba67cb00e1f
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/mdspan.cc
@@ -0,0 +1,603 @@
+// { dg-do run { target c++23 } }
+#include <mdspan>
+
+#include <testsuite_hooks.h>
+#include "extents/int_like.h"
+#include "layout_like.h"
+
+constexpr auto dyn = std::dynamic_extent;
+
+template<typename MDSpan, typename T, typename E, typename L =
std::layout_right,
+ typename A = std::default_accessor<T>>
+ constexpr void
+ assert_typedefs()
+ {
+ static_assert(std::same_as<typename MDSpan::extents_type, E>);
+ static_assert(std::same_as<typename MDSpan::layout_type, L>);
+ static_assert(std::same_as<typename MDSpan::accessor_type, A>);
+ static_assert(std::same_as<typename MDSpan::mapping_type,
+ typename L::mapping<E>>);
+ static_assert(std::same_as<typename MDSpan::element_type, T>);
+ static_assert(std::same_as<typename MDSpan::value_type,
+ std::remove_const_t<T>>);
+ static_assert(std::same_as<typename MDSpan::index_type,
+ typename E::index_type>);
+ static_assert(std::same_as<typename MDSpan::size_type,
+ typename E::size_type>);
+ static_assert(std::same_as<typename MDSpan::rank_type,
+ typename E::rank_type>);
+ static_assert(std::same_as<typename MDSpan::data_handle_type,
+ typename A::data_handle_type>);
+ static_assert(std::same_as<typename MDSpan::reference,
+ typename A::reference>);
+ }
+
+template<typename T, typename E, typename L, template<typename U> typename A>
+ constexpr void
+ test_typedefs()
+ { assert_typedefs<std::mdspan<T, E, L, A<T>>, T, E, L, A<T>>(); }
+
+constexpr void
+test_typedefs_all()
+{
+ using E = std::extents<int, 1, 2>;
+ using L = std::layout_left;
+
+ test_typedefs<double, E, L, std::default_accessor>();
+ test_typedefs<const double, E, L, std::default_accessor>();
+}
+
+template<typename MDSpan>
+ constexpr void
+ test_rank()
+ {
+ using Extents = typename MDSpan::extents_type;
+ static_assert(MDSpan::rank() == Extents::rank());
+ static_assert(MDSpan::rank_dynamic() == Extents::rank_dynamic());
+ }
+
+constexpr bool
+test_rank_all()
+{
+ test_rank<std::mdspan<double, std::extents<int>>>();
+ test_rank<std::mdspan<double, std::extents<int, 1>>>();
+ test_rank<std::mdspan<double, std::extents<int, dyn>>>();
+ return true;
+}
+
+template<typename Extents>
+ constexpr void
+ test_extent(Extents exts)
+ {
+ double data = 1.0;
+ auto md = std::mdspan(&data, exts);
+ using MDSpan = decltype(md);
+
+ for(size_t i = 0; i < MDSpan::rank(); ++i)
+ {
+ VERIFY(MDSpan::static_extent(i) == Extents::static_extent(i));
+ VERIFY(md.extent(i) == exts.extent(i));
+ }
+ }
+
+constexpr bool
+test_extent_all()
+{
+ // For rank == 0, check existence of the methods without calling them.
+ test_extent(std::extents<int>{});
+ test_extent(std::extents<int, 0>{});
+ test_extent(std::extents<int, dyn>{});
+ return true;
+}
+
+template<typename MDSpan>
+ constexpr void
+ test_class_properties()
+ {
+ static_assert(std::copyable<MDSpan>);
+ static_assert(std::is_nothrow_move_constructible_v<MDSpan>);
+ static_assert(std::is_nothrow_move_assignable_v<MDSpan>);
+ static_assert(std::is_nothrow_swappable_v<MDSpan>);
+ constexpr bool trivially_copyable =
+ std::is_trivially_copyable_v<typename MDSpan::accessor_type>
+ && std::is_trivially_copyable_v<typename MDSpan::mapping_type>
+ && std::is_trivially_copyable_v<typename MDSpan::data_handle_type>;
+ static_assert(std::is_trivially_copyable_v<MDSpan> == trivially_copyable);
+ }
+
+constexpr bool
+test_class_properties_all()
+{
+ test_class_properties<std::mdspan<double, std::extents<int>>>();
+ test_class_properties<std::mdspan<double, std::extents<int, 1>>>();
+ test_class_properties<std::mdspan<double, std::extents<int, dyn>>>();
+ return true;
+}
+
+constexpr bool
+test_default_ctor()
+{
+ static_assert(!std::is_default_constructible_v<std::mdspan<double,
+ std::extents<int>>>);
+ static_assert(!std::is_default_constructible_v<std::mdspan<double,
+ std::extents<int, 1>>>);
+ static_assert(std::is_default_constructible_v<std::mdspan<double,
+ std::extents<int, dyn>>>);
+
+ std::mdspan<double, std::extents<int, dyn>> md;
+ VERIFY(md.data_handle() == nullptr);
+ VERIFY(md.extent(0) == 0);
+ return true;
+}
+
+constexpr bool
+test_from_other()
+{
+ using Extents = std::extents<int, 3, 5, 7>;
+ auto exts = Extents{};
+
+ auto mapping = std::layout_right::mapping(exts);
+ constexpr size_t n = mapping.required_span_size();
+ std::array<double, n> storage{};
+
+ auto md1 = std::mdspan(storage.data(), exts);
+ auto md2 = std::mdspan<double, std::dextents<int, 3>>(md1);
+
+ VERIFY(md1.data_handle() == md2.data_handle());
+ VERIFY(md1.size() == md2.size());
+
+ static_assert(!std::is_convertible_v<
+ std::mdspan<double, std::extents<unsigned int, 2>>,
+ std::mdspan<double, std::extents<int, 2>>>);
+
+ static_assert(std::is_convertible_v<
+ std::mdspan<double, std::extents<int, 2>>,
+ std::mdspan<const double, std::extents<int, 2>>>);
+
+ static_assert(!std::is_constructible_v<
+ std::mdspan<double, std::extents<int, 2>>,
+ std::mdspan<const double, std::extents<int, 2>>>);
+
+ return true;
+}
+
+template<typename T, typename E, typename L = std::layout_right,
+ typename A = std::default_accessor<T>>
+ constexpr void
+ assert_deduced_typedefs(auto md)
+ { assert_typedefs<decltype(md), T, E, L, A>(); }
+
+constexpr bool
+test_from_carray()
+{
+ constexpr size_t n = 5;
+ double data[n] = {1.1, 2.2, 3.3, 4.4, 5.5};
+
+ auto md = std::mdspan(data);
+ assert_deduced_typedefs<double, std::extents<size_t, n>>(md);
+ VERIFY(md.rank() == 1);
+ VERIFY(md.rank_dynamic() == 0);
+ VERIFY(md[2] == data[2]);
+ return true;
+}
+
+constexpr bool
+test_from_pointer()
+{
+ double value = 12.3;
+ auto md = std::mdspan(&value);
+ assert_deduced_typedefs<double, std::extents<size_t>>(md);
+ VERIFY(md.rank() == 0);
+ VERIFY(md.rank_dynamic() == 0);
+ VERIFY(md[] == value);
+ return true;
+}
+
+constexpr bool
+test_from_pointer_and_shape()
+{
+ constexpr size_t n = 6;
+ std::array<double, n> data{1.1, 2.2, 3.3, 4.4, 5.5, 6.6};
+ std::array<int, 2> shape{2, 3};
+ std::span<const int, 2> shape_view(shape);
+
+ auto verify = [&data](auto md)
+ {
+ assert_deduced_typedefs<double, std::dextents<size_t, 2>>(md);
+ VERIFY(md.rank() == 2);
+ VERIFY(md.rank_dynamic() == 2);
+ VERIFY((md[0, 0]) == data[0]);
+ VERIFY((md[0, 1]) == data[1]);
+ VERIFY((md[1, 0]) == data[3]);
+ };
+
+ verify(std::mdspan(data.data(), shape[0], shape[1]));
+ verify(std::mdspan(data.data(), shape));
+ verify(std::mdspan(data.data(), shape_view));
+
+ std::mdspan<double, std::dextents<size_t, 2>> md1 = {data.data(), shape};
+ verify(md1);
+
+ std::mdspan<double, std::dextents<size_t, 2>> md2 = {data.data(),
shape_view};
+ verify(md2);
+
+ static_assert(std::is_constructible_v<
+ std::mdspan<float, std::extents<int, 3, 5>>, float*>);
+ static_assert(!std::is_constructible_v<
+ std::mdspan<float, std::extents<int, 3, 5>>, float*, int>);
+ static_assert(std::is_constructible_v<
+ std::mdspan<float, std::extents<int, 3, 5>>, float*, int, int>);
+ static_assert(std::is_constructible_v<
+ std::mdspan<float, std::extents<int, 3, 5>>, float*, std::span<int, 0>>);
+ static_assert(std::is_constructible_v<
+ std::mdspan<float, std::extents<int, 3, 5>>, float*, std::span<int, 2>>);
+ static_assert(!std::is_convertible_v<
+ float*, std::mdspan<float, std::extents<int, 3, 5>>>);
+
+ static_assert(std::is_constructible_v<
+ std::mdspan<float, std::dextents<int, 2>>, float*, std::span<int, 2>>);
+ static_assert(!std::is_constructible_v<
+ std::mdspan<float, std::dextents<int, 2>>, float*, std::span<int, 1>>);
+ static_assert(!std::is_constructible_v<
+ std::mdspan<float, std::dextents<int, 2>>, float*, std::span<int, 3>>);
+ static_assert(!std::is_constructible_v<
+ std::mdspan<float, std::dextents<int, 2>>, float*, std::span<int, dyn>>);
+ return true;
+}
+
+constexpr bool
+test_from_extents()
+{
+ constexpr size_t n = 3*5*7;
+ std::array<double, n> storage{};
+ using Extents = std::extents<int, 3, 5, 7>;
+ auto exts = Extents{};
+ auto md = std::mdspan(storage.data(), exts);
+
+ assert_deduced_typedefs<double, Extents>(md);
+ VERIFY(md.data_handle() == storage.data());
+ VERIFY(md.extents() == exts);
+ return true;
+}
+
+constexpr bool
+test_from_mapping()
+{
+ constexpr size_t n = 3*5*7;
+ std::array<double, n> storage{};
+ using Extents = std::extents<int, 3, 5, 7>;
+
+ auto exts = Extents{};
+ auto m = std::layout_left::mapping(exts);
+ auto md = std::mdspan(storage.data(), m);
+
+ assert_deduced_typedefs<double, Extents, std::layout_left>(md);
+ VERIFY(md.data_handle() == storage.data());
+ VERIFY(md.mapping() == m);
+ return true;
+}
+
+constexpr bool
+test_from_accessor()
+{
+ constexpr size_t n = 3*5*7;
+ std::array<double, n> storage{};
+ using Extents = std::extents<int, 3, 5, 7>;
+
+ auto exts = Extents{};
+ auto m = std::layout_left::mapping(exts);
+ auto a = std::default_accessor<double>{};
+ auto md = std::mdspan(storage.data(), m, a);
+
+ assert_deduced_typedefs<double, Extents, std::layout_left>(md);
+ VERIFY(md.data_handle() == storage.data());
+ VERIFY(md.mapping() == m);
+ return true;
+}
+
+void
+test_from_int_like()
+{
+ constexpr size_t n = 3*5*7;
+ std::array<double, n> storage{};
+
+ auto verify = [&](auto md)
+ {
+ VERIFY(md.data_handle() == storage.data());
+ VERIFY(md.extent(0) == 3);
+ VERIFY(md.extent(1) == 5);
+ VERIFY(md.extent(2) == 7);
+
+ VERIFY((md[IntLike(0), 0, IntLike(0)]) == 0.0);
+ auto zero = std::array{IntLike(0), IntLike(0), IntLike(0)};
+ auto zero_view = std::span<IntLike, 3>{zero};
+ VERIFY((md[zero]) == 0.0);
+ VERIFY((md[zero_view]) == 0.0);
+ };
+
+ auto shape = std::array{IntLike(3), IntLike(5), IntLike(7)};
+ auto shape_view = std::span<IntLike, 3>{shape};
+ verify(std::mdspan(storage.data(), IntLike(3), 5, IntLike(7)));
+ verify(std::mdspan(storage.data(), shape));
+ verify(std::mdspan(storage.data(), shape_view));
+}
+
+template<typename T, bool NothrowConstructible = true,
+ bool NothrowAssignable = true>
+ class OpaqueAccessor
+ {
+ struct Handle
+ {
+ constexpr
+ Handle(T * other)
+ : ptr(other)
+ { }
+
+ constexpr
+ Handle(const Handle&) noexcept(NothrowConstructible) = default;
+
+ constexpr
+ Handle(Handle&&) noexcept(NothrowConstructible) = default;
+
+ constexpr Handle&
+ operator=(const Handle&) noexcept(NothrowAssignable) = default;
+
+ constexpr Handle&
+ operator=(Handle&&) noexcept(NothrowAssignable) = default;
+
+ T * ptr;
+ };
+
+ public:
+ using element_type = T;
+ using reference = T&;
+ using data_handle_type = Handle;
+ using offset_policy = OpaqueAccessor;
+
+ reference
+ access(data_handle_type p, size_t i) const
+ {
+ ++access_count;
+ return p.ptr[i];
+ }
+
+ typename offset_policy::data_handle_type
+ offset(data_handle_type p, size_t i) const
+ {
+ ++offset_count;
+ return typename offset_policy::data_handle_type{(void*)(p.ptr + i)};
+ }
+
+ mutable size_t access_count = 0;
+ mutable size_t offset_count = 0;
+ };
+
+void
+test_from_opaque_accessor()
+{
+ constexpr size_t n = 3*5*7;
+ std::array<double, n> storage{};
+ using Extents = std::extents<int, 3, 5, 7>;
+
+ auto exts = Extents{};
+ auto m = std::layout_left::mapping(exts);
+ auto a = OpaqueAccessor<double>{};
+ auto handle = OpaqueAccessor<double>::data_handle_type{storage.data()};
+ auto md = std::mdspan(handle, m, a);
+
+ using MDSpan = decltype(md);
+ static_assert(std::same_as<MDSpan::accessor_type, decltype(a)>);
+
+ VERIFY((md[0, 0, 0]) == 0.0);
+ VERIFY(md.accessor().access_count == 1);
+
+ VERIFY((md[2, 4, 6]) == 0.0);
+ VERIFY(md.accessor().access_count == 2);
+}
+
+template<typename T, typename Base>
+ class BaseClassAccessor
+ {
+ public:
+ using element_type = T;
+ using reference = Base&;
+ using data_handle_type = T*;
+ using offset_policy = BaseClassAccessor;
+
+ static_assert(std::common_reference_with<reference&&, element_type&>);
+
+ reference
+ access(data_handle_type p, size_t i) const
+ { return p[i]; }
+
+ typename offset_policy::data_handle_type
+ offset(data_handle_type p, size_t i) const
+ { return typename offset_policy::data_handle_type{p + i}; }
+ };
+
+struct Base
+{
+ double value = 1.0;
+};
+
+struct Derived : Base
+{
+ double value = 2.0;
+};
+
+void
+test_from_base_class_accessor()
+{
+ constexpr size_t n = 3*5*7;
+ std::array<Derived, n> storage{};
+ using Extents = std::extents<int, 3, 5, 7>;
+
+ auto exts = Extents{};
+ auto m = std::layout_left::mapping(exts);
+ auto a = BaseClassAccessor<Derived, Base>{};
+ auto md = std::mdspan(storage.data(), m, a);
+
+ using MDSpan = decltype(md);
+ static_assert(std::same_as<MDSpan::accessor_type, decltype(a)>);
+ static_assert(std::same_as<decltype(md[0, 0, 0]), Base&>);
+ VERIFY((md[0, 0, 0].value) == 1.0);
+ VERIFY((md[2, 4, 6].value) == 1.0);
+}
+
+constexpr bool
+test_from_mapping_like()
+{
+ double data = 1.1;
+ auto m = LayoutLike::mapping<std::extents<int, 1, 2, 3>>{};
+ auto md = std::mdspan(&data, m);
+ VERIFY((md[0, 0, 0]) == data);
+ VERIFY((md[0, 1, 2]) == data);
+ return true;
+}
+
+template<typename MDSpan>
+ constexpr void
+ test_empty(MDSpan md)
+ {
+ VERIFY(md.empty() == (md.size() == 0));
+ }
+
+constexpr bool
+test_empty_all()
+{
+ test_empty(std::mdspan<double, std::extents<int, dyn>>{});
+ return true;
+}
+
+constexpr bool
+test_access()
+{
+ using Extents = std::extents<int, 3, 5, 7>;
+ auto exts = Extents{};
+
+ auto mapping = std::layout_left::mapping(exts);
+ constexpr size_t n = mapping.required_span_size();
+ std::array<double, n> storage{};
+
+ auto md = std::mdspan(storage.data(), mapping);
+ static_assert(std::__mdspan::__mapping_alike<decltype(md)>);
+
+ for(int i = 0; i < exts.extent(0); ++i)
+ for(int j = 0; j < exts.extent(1); ++j)
+ for(int k = 0; k < exts.extent(2); ++k)
+ {
+ std::array<int, 3> ijk{i, j, k};
+ storage[mapping(i, j, k)] = 1.0;
+ VERIFY((md[i, j, k]) == 1.0);
+ VERIFY((md[ijk]) == 1.0);
+ VERIFY((md[std::span(ijk)]) == 1.0);
+ storage[mapping(i, j, k)] = 0.0;
+ }
+ return true;
+}
+
+constexpr bool
+test_swap()
+{
+ using Extents = std::dextents<int, 2>;
+ auto e1 = Extents{3, 5};
+ auto e2 = Extents{7, 11};
+
+ std::array<double, 3*5> s1{};
+ std::array<double, 7*11> s2{};
+
+ auto md1 = std::mdspan(s1.data(), e1);
+ auto md2 = std::mdspan(s2.data(), e2);
+
+ std::swap(md1, md2);
+
+ VERIFY(md1.data_handle() == s2.data());
+ VERIFY(md2.data_handle() == s1.data());
+
+ VERIFY(md1.size() == s2.size());
+ VERIFY(md2.size() == s1.size());
+ return true;
+}
+
+template<bool Constructible, bool Assignable>
+constexpr void
+test_nothrow_movable()
+{
+ using Layout = std::layout_left;
+ using Extents = std::dextents<int, 3>;
+ using Accessor = OpaqueAccessor<int, Constructible, Assignable>;
+ using Handle = Accessor::data_handle_type;
+ static_assert(std::is_nothrow_move_assignable_v<Accessor>);
+ static_assert(std::is_nothrow_move_constructible_v<Accessor>);
+ static_assert(std::is_nothrow_move_assignable_v<Handle> == Assignable);
+ static_assert(std::is_nothrow_move_constructible_v<Handle> == Constructible);
+
+ using MDSpan = std::mdspan<int, Extents, Layout, Accessor>;
+ static_assert(std::is_nothrow_move_assignable_v<MDSpan> == Assignable);
+ static_assert(std::is_nothrow_move_constructible_v<MDSpan> == Constructible);
+}
+
+constexpr void
+test_nothrow_movable_all()
+{
+ using MDSpan = std::mdspan<double, std::dextents<int, 3>>;
+ static_assert(std::is_nothrow_move_assignable_v<MDSpan>);
+ static_assert(std::is_nothrow_move_constructible_v<MDSpan>);
+
+ test_nothrow_movable<true, true>();
+ test_nothrow_movable<true, false>();
+ test_nothrow_movable<false, true>();
+ test_nothrow_movable<false, false>();
+}
+
+int
+main()
+{
+ test_typedefs_all();
+
+ test_rank_all();
+ test_extent_all();
+ static_assert(test_extent_all());
+
+ test_class_properties_all();
+ static_assert(test_class_properties_all());
+
+ test_empty_all();
+ static_assert(test_empty_all());
+
+ test_default_ctor();
+ static_assert(test_default_ctor());
+
+ test_from_other();
+ static_assert(test_from_other());
+
+ test_from_carray();
+ static_assert(test_from_carray());
+
+ test_from_pointer_and_shape();
+ static_assert(test_from_pointer_and_shape());
+
+ test_from_extents();
+ static_assert(test_from_extents());
+
+ test_from_mapping();
+ static_assert(test_from_mapping());
+
+ test_from_accessor();
+ static_assert(test_from_accessor());
+
+ test_from_int_like();
+ test_from_opaque_accessor();
+ test_from_base_class_accessor();
+ test_from_mapping_like();
+ static_assert(test_from_mapping_like());
+
+ test_access();
+ static_assert(test_access());
+
+ test_swap();
+ static_assert(test_swap());
+
+ test_nothrow_movable_all();
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/out_of_bounds_neg.cc
b/libstdc++-v3/testsuite/23_containers/mdspan/out_of_bounds_neg.cc
new file mode 100644
index 00000000000..dceae56e9d9
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/out_of_bounds_neg.cc
@@ -0,0 +1,24 @@
+// { dg-do compile { target c++23 } }
+#include<mdspan>
+
+#include "layout_like.h"
+
+template<typename Layout>
+constexpr bool
+test_invalid_multi_index()
+{
+
+ double data = 1.1;
+ auto m = typename Layout::mapping<std::extents<int, 1, 2, 3>>{};
+ auto md = std::mdspan(&data, m);
+
+ [[maybe_unused]] double x = md[0, 2, 2]; // { dg-error "expansion of" }
+ return true;
+};
+static_assert(test_invalid_multi_index<LayoutLike>()); // { dg-error "expansion
of" }
+static_assert(test_invalid_multi_index<std::layout_left>()); // { dg-error
"expansion of" }
+static_assert(test_invalid_multi_index<std::layout_right>()); // { dg-error
"expansion of" }
+static_assert(test_invalid_multi_index<std::layout_stride>()); // { dg-error
"expansion of" }
+
+// { dg-prune-output "non-constant condition" }
+// { dg-prune-output "__glibcxx_assert" }
--
2.49.0