[libcxx-commits] [libcxx] 8cc944c - [libc++][mdspan] P3383R3: `mdspan.at()` (#175213)
via libcxx-commits
libcxx-commits at lists.llvm.org
Fri May 22 04:54:33 PDT 2026
Author: eiytoq
Date: 2026-05-22T19:54:28+08:00
New Revision: 8cc944cb29cec7974509a34ae8ad85c483cbd120
URL: https://github.com/llvm/llvm-project/commit/8cc944cb29cec7974509a34ae8ad85c483cbd120
DIFF: https://github.com/llvm/llvm-project/commit/8cc944cb29cec7974509a34ae8ad85c483cbd120.diff
LOG: [libc++][mdspan] P3383R3: `mdspan.at()` (#175213)
Implements https://wg21.link/P3383R3
Closes #148149
---------
Co-authored-by: A. Jiang <de34 at live.cn>
Added:
libcxx/test/libcxx/containers/views/mdspan/mdspan/assert.at.pass.cpp
libcxx/test/std/containers/views/mdspan/mdspan/at.pass.cpp
Modified:
libcxx/docs/ReleaseNotes/23.rst
libcxx/docs/Status/Cxx2cPapers.csv
libcxx/include/__mdspan/mdspan.h
libcxx/include/mdspan
libcxx/test/libcxx/containers/views/mdspan/nodiscard.verify.cpp
libcxx/utils/generate_feature_test_macro_components.py
Removed:
################################################################################
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index a55869a8bf783..7f5068214ca57 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -48,6 +48,7 @@ Implemented Papers
- P2164R9: ``views::enumerate`` (`Github <https://llvm.org/PR105251>`__)
- P2322R6: ``ranges::fold`` (`Github <https://llvm.org/PR105208>`__)
- P4144R1: Remove ``span``'s ``initializer_list`` constructor for C++26 (`Github <https://llvm.org/PR189612>`__)
+- P3383R3: ``mdspan.at()`` (`Github <https://llvm.org/PR175213>`__)
Improvements and New Features
-----------------------------
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index eb15fac940f5c..2eee4321b023a 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -134,7 +134,7 @@
"`P3480R6 <https://wg21.link/P3480R6>`__","``std::simd`` is a range","2025-06 (Sofia)","","","`#148144 <https://github.com/llvm/llvm-project/issues/148144>`__",""
"`P2664R11 <https://wg21.link/P2664R11>`__","Extend ``std::simd`` with permutation API","2025-06 (Sofia)","","","`#148145 <https://github.com/llvm/llvm-project/issues/148145>`__",""
"`P3691R1 <https://wg21.link/P3691R1>`__","Reconsider naming of the namespace for ``std::simd``","2025-06 (Sofia)","","","`#148148 <https://github.com/llvm/llvm-project/issues/148148>`__",""
-"`P3383R3 <https://wg21.link/P3383R3>`__","``mdspan.at()``","2025-06 (Sofia)","","","`#148149 <https://github.com/llvm/llvm-project/issues/148149>`__",""
+"`P3383R3 <https://wg21.link/P3383R3>`__","``mdspan.at()``","2025-06 (Sofia)","|Complete|","23","`#148149 <https://github.com/llvm/llvm-project/issues/148149>`__",""
"`P2927R3 <https://wg21.link/P2927R3>`__","Inspecting ``exception_ptr``","2025-06 (Sofia)","","","`#148150 <https://github.com/llvm/llvm-project/issues/148150>`__",""
"`P3748R0 <https://wg21.link/P3748R0>`__","Inspecting ``exception_ptr`` should be constexpr","2025-06 (Sofia)","","","`#148151 <https://github.com/llvm/llvm-project/issues/148151>`__",""
"`P2830R10 <https://wg21.link/P2830R10>`__","Standardized Constexpr Type Ordering","2025-06 (Sofia)","","","`#148152 <https://github.com/llvm/llvm-project/issues/148152>`__",""
diff --git a/libcxx/include/__mdspan/mdspan.h b/libcxx/include/__mdspan/mdspan.h
index 7f71b0750a78d..bcfa302231cd4 100644
--- a/libcxx/include/__mdspan/mdspan.h
+++ b/libcxx/include/__mdspan/mdspan.h
@@ -38,9 +38,11 @@
#include <__type_traits/remove_cv.h>
#include <__type_traits/remove_pointer.h>
#include <__type_traits/remove_reference.h>
+#include <__utility/as_const.h>
#include <__utility/integer_sequence.h>
#include <array>
#include <span>
+#include <stdexcept>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@@ -217,6 +219,37 @@ class mdspan {
}(make_index_sequence<rank()>()));
}
+# if _LIBCPP_STD_VER >= 26
+ template <class... _OtherIndexTypes>
+ requires((is_convertible_v<_OtherIndexTypes, index_type> && ...) &&
+ (is_nothrow_constructible_v<index_type, _OtherIndexTypes> && ...) &&
+ (sizeof...(_OtherIndexTypes) == rank()))
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr reference at(_OtherIndexTypes... __indices) const {
+ if (!__mdspan_detail::__is_multidimensional_index_in(__map_.extents(), __indices...)) {
+ __throw_out_of_range();
+ }
+ return (*this)[extents_type::__index_cast(std::move(__indices))...];
+ }
+
+ template <class _OtherIndexType>
+ requires(is_convertible_v<const _OtherIndexType&, index_type> &&
+ is_nothrow_constructible_v<index_type, const _OtherIndexType&>)
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr reference at(const array<_OtherIndexType, rank()>& __indices) const {
+ return [&]<size_t... _Idxs>(index_sequence<_Idxs...>) -> reference {
+ return at(extents_type::__index_cast(__indices[_Idxs])...);
+ }(make_index_sequence<rank()>());
+ }
+
+ template <class _OtherIndexType>
+ requires(is_convertible_v<const _OtherIndexType&, index_type> &&
+ is_nothrow_constructible_v<index_type, const _OtherIndexType&>)
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr reference at(span<_OtherIndexType, rank()> __indices) const {
+ return [&]<size_t... _Idxs>(index_sequence<_Idxs...>) -> reference {
+ return at(extents_type::__index_cast(std::as_const(__indices[_Idxs]))...);
+ }(make_index_sequence<rank()>());
+ }
+# endif
+
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr size_type size() const noexcept {
// Could leave this as only checked in debug mode: semantically size() is never
// guaranteed to be related to any accessible range
@@ -273,6 +306,10 @@ class mdspan {
template <class, class, class, class>
friend class mdspan;
+
+# if _LIBCPP_STD_VER >= 26
+ _LIBCPP_HIDE_FROM_ABI void __throw_out_of_range() const { std::__throw_out_of_range("mdspan"); }
+# endif
};
# if _LIBCPP_STD_VER >= 26
diff --git a/libcxx/include/mdspan b/libcxx/include/mdspan
index 30a17e4299068..2f0e1f9b87751 100644
--- a/libcxx/include/mdspan
+++ b/libcxx/include/mdspan
@@ -361,6 +361,13 @@ namespace std {
template<class OtherIndexType>
constexpr reference operator[](const array<OtherIndexType, rank()>& indices) const;
+ template<class... OtherIndexTypes>
+ constexpr reference at(OtherIndexTypes... indices) const;
+ template<class OtherIndexType>
+ constexpr reference at(span<OtherIndexType, rank()> indices) const;
+ template<class OtherIndexType>
+ constexpr reference at(const array<OtherIndexType, rank()>& indices) const;
+
constexpr size_type size() const noexcept;
constexpr bool empty() const noexcept;
diff --git a/libcxx/test/libcxx/containers/views/mdspan/mdspan/assert.at.pass.cpp b/libcxx/test/libcxx/containers/views/mdspan/mdspan/assert.at.pass.cpp
new file mode 100644
index 0000000000000..53bc50addae1b
--- /dev/null
+++ b/libcxx/test/libcxx/containers/views/mdspan/mdspan/assert.at.pass.cpp
@@ -0,0 +1,112 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: has-unix-headers
+// REQUIRES: std-at-least-c++26
+// UNSUPPORTED: no-exceptions
+
+// <mdspan>
+
+// template<class... OtherIndexTypes>
+// constexpr reference at(OtherIndexTypes... indices) const;
+//
+// Constraints:
+// - (is_convertible_v<OtherIndexTypes, index_type> && ...) is true,
+// - (is_nothrow_constructible_v<index_type, OtherIndexTypes> && ...) is true, and
+// - sizeof...(OtherIndexTypes) == rank() is true.
+//
+// template<class OtherIndexType>
+// constexpr reference at(span<OtherIndexType, rank()> indices) const;
+//
+// template<class OtherIndexType>
+// constexpr reference at(const array<OtherIndexType, rank()>& indices) const;
+//
+// Constraints:
+// - is_convertible_v<const OtherIndexType&, index_type> is true, and
+// - is_nothrow_constructible_v<index_type, const OtherIndexType&> is true.
+//
+// Throws:
+// - std::out_of_range if extents_type::index-cast(indices) is not a multidimensional index in extents_.
+
+#include <mdspan>
+#include <string_view>
+#include <cassert>
+
+#include "test_macros.h"
+
+template <typename F>
+void test(F&& f) {
+ try {
+ f();
+ assert(false && "Unexpected");
+ } catch (const std::out_of_range& e) {
+ LIBCPP_ASSERT(std::string_view(e.what()).contains("mdspan"));
+ } catch (...) {
+ assert(false && "Unexpected");
+ }
+}
+
+int main() {
+ float data[1024];
+ // value out of range
+ {
+ std::mdspan m(data, std::extents<unsigned char, 5>());
+ test([&] { TEST_IGNORE_NODISCARD m.at(-1); });
+ test([&] { TEST_IGNORE_NODISCARD m.at(-130); });
+ test([&] { TEST_IGNORE_NODISCARD m.at(5); });
+ test([&] { TEST_IGNORE_NODISCARD m.at(256); });
+ test([&] { TEST_IGNORE_NODISCARD m.at(1000); });
+ }
+ {
+ std::mdspan m(data, std::extents<signed char, 5>());
+ test([&] { TEST_IGNORE_NODISCARD m.at(-1); });
+ test([&] { TEST_IGNORE_NODISCARD m.at(-130); });
+ test([&] { TEST_IGNORE_NODISCARD m.at(5); });
+ test([&] { TEST_IGNORE_NODISCARD m.at(128); });
+ test([&] { TEST_IGNORE_NODISCARD m.at(1000); });
+ }
+ {
+ std::mdspan m(data, std::dextents<unsigned char, 1>(5));
+ test([&] { TEST_IGNORE_NODISCARD m.at(-1); });
+ test([&] { TEST_IGNORE_NODISCARD m.at(-130); });
+ test([&] { TEST_IGNORE_NODISCARD m.at(5); });
+ test([&] { TEST_IGNORE_NODISCARD m.at(256); });
+ test([&] { TEST_IGNORE_NODISCARD m.at(1000); });
+ }
+ {
+ std::mdspan m(data, std::dextents<signed char, 1>(5));
+ test([&] { TEST_IGNORE_NODISCARD m.at(-1); });
+ test([&] { TEST_IGNORE_NODISCARD m.at(-130); });
+ test([&] { TEST_IGNORE_NODISCARD m.at(5); });
+ test([&] { TEST_IGNORE_NODISCARD m.at(128); });
+ test([&] { TEST_IGNORE_NODISCARD m.at(1000); });
+ }
+ {
+ std::mdspan m(data, std::dextents<int, 3>(5, 7, 9));
+ test([&] { TEST_IGNORE_NODISCARD m.at(-1, -1, -1); });
+ test([&] { TEST_IGNORE_NODISCARD m.at(-1, 0, 0); });
+ test([&] { TEST_IGNORE_NODISCARD m.at(0, -1, 0); });
+ test([&] { TEST_IGNORE_NODISCARD m.at(0, 0, -1); });
+ test([&] { TEST_IGNORE_NODISCARD m.at(5, 3, 3); });
+ test([&] { TEST_IGNORE_NODISCARD m.at(3, 7, 3); });
+ test([&] { TEST_IGNORE_NODISCARD m.at(3, 3, 9); });
+ test([&] { TEST_IGNORE_NODISCARD m.at(5, 7, 9); });
+ }
+ {
+ std::mdspan m(data, std::dextents<unsigned, 3>(5, 7, 9));
+ test([&] { TEST_IGNORE_NODISCARD m.at(-1, -1, -1); });
+ test([&] { TEST_IGNORE_NODISCARD m.at(-1, 0, 0); });
+ test([&] { TEST_IGNORE_NODISCARD m.at(0, -1, 0); });
+ test([&] { TEST_IGNORE_NODISCARD m.at(0, 0, -1); });
+ test([&] { TEST_IGNORE_NODISCARD m.at(5, 3, 3); });
+ test([&] { TEST_IGNORE_NODISCARD m.at(3, 7, 3); });
+ test([&] { TEST_IGNORE_NODISCARD m.at(3, 3, 9); });
+ test([&] { TEST_IGNORE_NODISCARD m.at(5, 7, 9); });
+ }
+ return 0;
+}
diff --git a/libcxx/test/libcxx/containers/views/mdspan/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/views/mdspan/nodiscard.verify.cpp
index d248656ec0ae5..f17ef6ee870d4 100644
--- a/libcxx/test/libcxx/containers/views/mdspan/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/views/mdspan/nodiscard.verify.cpp
@@ -16,6 +16,8 @@
#include <mdspan>
#include <span>
+#include "test_macros.h"
+
void test() {
// mdspan<>
@@ -28,6 +30,12 @@ void test() {
std::span sp{arr};
mdsp[sp]; // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#if TEST_STD_VER >= 26
+ mdsp.at(0, 1); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+ mdsp.at(arr); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+ mdsp.at(sp); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
+
mdsp.rank(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
mdsp.rank_dynamic(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
mdsp.static_extent(0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/std/containers/views/mdspan/mdspan/at.pass.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/at.pass.cpp
new file mode 100644
index 0000000000000..d52923cd4b133
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/at.pass.cpp
@@ -0,0 +1,327 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: std-at-least-c++26
+
+// <mdspan>
+
+// template<class... OtherIndexTypes>
+// constexpr reference at(OtherIndexTypes... indices) const;
+//
+// Constraints:
+// - (is_convertible_v<OtherIndexTypes, index_type> && ...) is true,
+// - (is_nothrow_constructible_v<index_type, OtherIndexTypes> && ...) is true, and
+// - sizeof...(OtherIndexTypes) == rank() is true.
+//
+// template<class OtherIndexType>
+// constexpr reference at(span<OtherIndexType, rank()> indices) const;
+//
+// template<class OtherIndexType>
+// constexpr reference at(const array<OtherIndexType, rank()>& indices) const;
+//
+// Constraints:
+// - is_convertible_v<const OtherIndexType&, index_type> is true, and
+// - is_nothrow_constructible_v<index_type, const OtherIndexType&> is true.
+//
+// Throws:
+// - std::out_of_range if extents_type::index-cast(indices) is not a multidimensional index in extents_.
+
+#include <array>
+#include <cassert>
+#include <mdspan>
+#include <span>
+#include <vector>
+
+#include "assert_macros.h"
+
+#include "../ConvertibleToIntegral.h"
+#include "../CustomTestLayouts.h"
+
+struct SpyIndex {
+ int val;
+ constexpr SpyIndex(int v) : val(v) {}
+ constexpr operator int() const noexcept { return val; }
+};
+
+struct CvIndex {
+ constexpr operator int() noexcept { return 4; }
+ constexpr operator int() const noexcept { return 3; }
+};
+
+class strict_cast_layout {
+public:
+ template <class Extents>
+ class mapping;
+};
+
+template <class Extents>
+class strict_cast_layout::mapping {
+public:
+ using extents_type = Extents;
+ using index_type = extents_type::index_type;
+ using size_type = extents_type::size_type;
+ using rank_type = extents_type::rank_type;
+ using layout_type = strict_cast_layout;
+
+ constexpr mapping() noexcept = default;
+ constexpr mapping(const mapping&) noexcept = default;
+ constexpr mapping(const extents_type& ext) noexcept : extents_(ext) {}
+
+ template <class OtherExtents>
+ constexpr mapping(const mapping<OtherExtents>& other) noexcept : extents_(other.extents()) {}
+
+ constexpr mapping& operator=(const mapping&) noexcept = default;
+
+ constexpr const extents_type& extents() const noexcept { return extents_; }
+
+ constexpr index_type required_span_size() const noexcept {
+ index_type size = 1;
+ for (rank_type r = 0; r < extents_type::rank(); ++r)
+ size *= extents_.extent(r);
+ return size;
+ }
+
+ template <std::integral... Indices>
+ requires(sizeof...(Indices) == extents_type::rank())
+ constexpr index_type operator()(Indices... idx) const noexcept {
+ return [&]<size_t... _Is>(std::index_sequence<_Is...>) {
+ index_type res = 0;
+ std::array<index_type, sizeof...(Indices)> idx_arr{static_cast<index_type>(idx)...};
+ ((res = res * extents_.extent(_Is) + idx_arr[_Is]), ...);
+ return res;
+ }(std::make_index_sequence<sizeof...(Indices)>{});
+ }
+
+ constexpr index_type operator()(SpyIndex) const noexcept = delete;
+
+ static constexpr bool is_always_unique() noexcept { return true; }
+ static constexpr bool is_always_exhaustive() noexcept { return true; }
+ static constexpr bool is_always_strided() noexcept { return true; }
+
+ static constexpr bool is_unique() noexcept { return true; }
+ static constexpr bool is_exhaustive() noexcept { return true; }
+ static constexpr bool is_strided() noexcept { return true; }
+
+ constexpr index_type stride(rank_type r) const noexcept {
+ index_type stride = 1;
+ for (rank_type i = r + 1; i < extents_type::rank(); ++i)
+ stride *= extents_.extent(i);
+ return stride;
+ }
+
+ friend constexpr bool operator==(const mapping& lhs, const mapping& rhs) noexcept {
+ return lhs.extents_ == rhs.extents_;
+ }
+
+private:
+ extents_type extents_{};
+};
+
+template <class MDS, class... Indices>
+concept at_constraints = requires(MDS m, Indices... idxs) {
+ { m.at(idxs...) } -> std::same_as<typename MDS::reference>;
+};
+
+template <class MDS, class... Indices>
+ requires(at_constraints<MDS, Indices...>)
+constexpr bool check_at_constraints(MDS m, Indices... idxs) {
+ TEST_IGNORE_NODISCARD m.at(idxs...);
+ return true;
+}
+
+template <class MDS, class... Indices>
+constexpr bool check_at_constraints(MDS, Indices...) {
+ return false;
+}
+
+template <class MDS, class... Args>
+constexpr void iterate(MDS mds, Args... args) {
+ constexpr int r = static_cast<int>(MDS::extents_type::rank()) - 1 - static_cast<int>(sizeof...(Args));
+
+ if constexpr (r == -1) {
+ std::same_as<typename MDS::reference> decltype(auto) ptr_accessor =
+ mds.accessor().access(mds.data_handle(), mds.mapping()(args...));
+ std::array<typename MDS::index_type, MDS::rank()> args_arr{static_cast<MDS::index_type>(args)...};
+
+ // mdspan.at(indices...)
+ std::same_as<typename MDS::reference> decltype(auto) ptr_at = mds.at(args...);
+ assert(&ptr_at == &ptr_accessor);
+
+ // mdspan.at(array)
+ std::same_as<typename MDS::reference> decltype(auto) ptr_arr = mds.at(args_arr);
+ assert(&ptr_arr == &ptr_accessor);
+
+ // mdspan.at(span)
+ std::same_as<typename MDS::reference> decltype(auto) ptr_span = mds.at(std::span(args_arr));
+ assert(&ptr_span == &ptr_accessor);
+
+ } else {
+ for (typename MDS::index_type i = 0; i < mds.extents().extent(r); ++i) {
+ iterate(mds, i, args...);
+ }
+ }
+}
+
+template <class Mapping>
+constexpr void test_iteration(Mapping m) {
+ std::array<int, 1024> data;
+ using MDS = std::mdspan<int, typename Mapping::extents_type, typename Mapping::layout_type>;
+ MDS mds(data.data(), m);
+ iterate(mds);
+}
+
+template <class Layout>
+constexpr void test_layout() {
+ test_iteration(construct_mapping(Layout{}, std::extents<int>{}));
+ test_iteration(construct_mapping(Layout{}, std::dextents<unsigned, 1>{1}));
+ test_iteration(construct_mapping(Layout{}, std::dextents<unsigned, 1>{7}));
+ test_iteration(construct_mapping(Layout{}, std::extents<unsigned, 7>{}));
+ test_iteration(construct_mapping(Layout{}, std::extents<unsigned, 7, 8>{}));
+ test_iteration(construct_mapping(Layout{}, std::dextents<signed char, 4>{1, 1, 1, 1}));
+
+ int data[1];
+ // Check at constraint for number of arguments
+ static_assert(check_at_constraints(std::mdspan{data, construct_mapping(Layout{}, std::dextents<int, 1>{1})}, 0));
+ static_assert(!check_at_constraints(std::mdspan{data, construct_mapping(Layout{}, std::dextents<int, 1>{1})}, 0, 0));
+
+ // Check at constraint for convertibility of arguments to index_type
+ static_assert(
+ check_at_constraints(std::mdspan{data, construct_mapping(Layout{}, std::dextents<int, 1>{1})}, IntType{0}));
+ static_assert(
+ !check_at_constraints(std::mdspan{data, construct_mapping(Layout{}, std::dextents<unsigned, 1>{1})}, IntType{0}));
+
+ // Check at constraint for no-throw-constructibility of index_type from arguments
+ static_assert(!check_at_constraints(
+ std::mdspan{data, construct_mapping(Layout{}, std::dextents<unsigned char, 1>{1})}, IntType{0}));
+
+ // Check that mixed integrals work: note the second one tests that mdspan casts: layout_wrapping_integral does not accept IntType
+ static_assert(check_at_constraints(
+ std::mdspan{data, construct_mapping(Layout{}, std::dextents<unsigned char, 2>{1, 1})}, 0, 0uz));
+ static_assert(check_at_constraints(
+ std::mdspan{data, construct_mapping(Layout{}, std::dextents<int, 2>{1, 1})}, 0u, IntType{0}));
+
+ constexpr bool t = true;
+ constexpr bool o = false;
+ static_assert(!check_at_constraints(
+ std::mdspan{data, construct_mapping(Layout{}, std::dextents<int, 2>{1, 1})}, 0uz, IntConfig<o, o, t, t>{0}));
+ static_assert(check_at_constraints(
+ std::mdspan{data, construct_mapping(Layout{}, std::dextents<int, 2>{1, 1})}, 0uz, IntConfig<o, t, t, t>{0}));
+ static_assert(check_at_constraints(
+ std::mdspan{data, construct_mapping(Layout{}, std::dextents<int, 2>{1, 1})}, 0uz, IntConfig<o, t, o, t>{0}));
+ static_assert(!check_at_constraints(
+ std::mdspan{data, construct_mapping(Layout{}, std::dextents<int, 2>{1, 1})}, 0uz, IntConfig<t, o, o, t>{0}));
+ static_assert(check_at_constraints(
+ std::mdspan{data, construct_mapping(Layout{}, std::dextents<int, 2>{1, 1})}, 0uz, IntConfig<t, o, t, o>{0}));
+ static_assert(check_at_constraints(
+ std::mdspan{data, construct_mapping(Layout{}, std::dextents<int, 2>{1, 1})}, 0uz, IntConfig<t, t, t, t>{0}));
+
+ // layout_wrapped wouldn't quite work here the way we wrote the check
+ // IntConfig has configurable conversion properties: convert from const&, convert from non-const, no-throw-ctor from const&, no-throw-ctor from non-const
+ if constexpr (std::is_same_v<Layout, std::layout_left>) {
+ static_assert(!check_at_constraints(std::mdspan{data, construct_mapping(Layout{}, std::dextents<int, 1>{1})},
+ std::array{IntConfig<o, o, t, t>{0}}));
+ static_assert(!check_at_constraints(std::mdspan{data, construct_mapping(Layout{}, std::dextents<int, 1>{1})},
+ std::array{IntConfig<o, t, t, t>{0}}));
+ static_assert(!check_at_constraints(std::mdspan{data, construct_mapping(Layout{}, std::dextents<int, 1>{1})},
+ std::array{IntConfig<t, o, o, t>{0}}));
+ static_assert(!check_at_constraints(std::mdspan{data, construct_mapping(Layout{}, std::dextents<int, 1>{1})},
+ std::array{IntConfig<t, t, o, t>{0}}));
+ static_assert(check_at_constraints(std::mdspan{data, construct_mapping(Layout{}, std::dextents<int, 1>{1})},
+ std::array{IntConfig<t, o, t, o>{0}}));
+ static_assert(check_at_constraints(std::mdspan{data, construct_mapping(Layout{}, std::dextents<int, 1>{1})},
+ std::array{IntConfig<t, t, t, t>{0}}));
+
+ {
+ std::array idx{IntConfig<o, o, t, t>{0}};
+ assert(!check_at_constraints(
+ std::mdspan{data, construct_mapping(Layout{}, std::dextents<int, 1>{1})}, std::span{idx}));
+ }
+ {
+ std::array idx{IntConfig<o, t, t, t>{0}};
+ assert(!check_at_constraints(
+ std::mdspan{data, construct_mapping(Layout{}, std::dextents<int, 1>{1})}, std::span{idx}));
+ }
+ {
+ std::array idx{IntConfig<t, o, o, t>{0}};
+ assert(!check_at_constraints(
+ std::mdspan{data, construct_mapping(Layout{}, std::dextents<int, 1>{1})}, std::span{idx}));
+ }
+ {
+ std::array idx{IntConfig<t, t, o, t>{0}};
+ assert(!check_at_constraints(
+ std::mdspan{data, construct_mapping(Layout{}, std::dextents<int, 1>{1})}, std::span{idx}));
+ }
+ {
+ std::array idx{IntConfig<t, o, t, o>{0}};
+ assert(check_at_constraints(
+ std::mdspan{data, construct_mapping(Layout{}, std::dextents<int, 1>{1})}, std::span{idx}));
+ }
+ {
+ std::array idx{IntConfig<t, t, t, t>{0}};
+ assert(check_at_constraints(
+ std::mdspan{data, construct_mapping(Layout{}, std::dextents<int, 1>{1})}, std::span{idx}));
+ }
+ }
+}
+
+constexpr bool test() {
+ test_layout<std::layout_left>();
+ test_layout<std::layout_right>();
+ test_layout<layout_wrapping_integral<4>>();
+ return true;
+}
+
+constexpr bool test_cast() {
+ using MDS = std::mdspan<int, std::dextents<int, 1>, strict_cast_layout>;
+
+ std::array data{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+ const MDS m{data.data(), std::dextents<int, 1>{10}};
+
+ SpyIndex index(3);
+ std::array indices{index};
+
+ assert(&m.at(index) == &data[3]);
+ assert(&m.at(indices) == &data[3]);
+ assert(&m.at(std::span{indices}) == &data[3]);
+
+ std::array cv_indices{CvIndex{}};
+ assert(&m.at(CvIndex{}) == &data[4]);
+ assert(&m.at(cv_indices) == &data[3]);
+ assert(&m.at(std::span{cv_indices}) == &data[3]);
+
+ return true;
+}
+
+static void test_throws() {
+ std::array<int, 100> data{};
+ std::mdspan m(data.data(), 10, 10);
+
+ assert(&m.at(9, 9) == &data[99]);
+
+ TEST_THROWS_TYPE(std::out_of_range, m.at(10, 0));
+ TEST_THROWS_TYPE(std::out_of_range, m.at(0, 10));
+ TEST_THROWS_TYPE(std::out_of_range, m.at(-1, 0));
+
+ [[maybe_unused]] std::array bad_row{10, 0};
+ TEST_THROWS_TYPE(std::out_of_range, m.at(bad_row));
+
+ [[maybe_unused]] std::array bad_col{0, 10};
+ TEST_THROWS_TYPE(std::out_of_range, m.at(std::span{bad_col}));
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ test_cast();
+ static_assert(test_cast());
+
+ test_throws();
+
+ return 0;
+}
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 2c1f92992bf1a..f81eb0e0e8060 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -957,7 +957,7 @@ def add_version_header(tc):
"name": "__cpp_lib_mdspan",
"values": {
"c++23": 202207,
- "c++26": 202406, # P2389R2 dextents Index Type Parameter
+ "c++26": 202406,
},
"headers": ["mdspan"],
},
More information about the libcxx-commits
mailing list