Skip to content

[libc++][mdspan] P3383R3: mdspan.at()#175213

Merged
eiytoq merged 9 commits into
llvm:mainfrom
eiytoq:libcxx-p3383-mdspan-at
May 22, 2026
Merged

[libc++][mdspan] P3383R3: mdspan.at()#175213
eiytoq merged 9 commits into
llvm:mainfrom
eiytoq:libcxx-p3383-mdspan-at

Conversation

@eiytoq

@eiytoq eiytoq commented Jan 9, 2026

Copy link
Copy Markdown
Contributor

@eiytoq eiytoq requested a review from a team as a code owner January 9, 2026 17:42
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Jan 9, 2026
@llvmbot

llvmbot commented Jan 9, 2026

Copy link
Copy Markdown
Member

@llvm/pr-subscribers-libcxx

Author: eiytoq (eiytoq)

Changes

Implements https://wg21.link/P3383R3

Closes #148149


Patch is 23.74 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/175213.diff

11 Files Affected:

  • (modified) libcxx/docs/FeatureTestMacroTable.rst (+1-1)
  • (modified) libcxx/docs/Status/Cxx2cPapers.csv (+1-1)
  • (modified) libcxx/include/__mdspan/mdspan.h (+38)
  • (modified) libcxx/include/mdspan (+7)
  • (modified) libcxx/include/version (+2-2)
  • (modified) libcxx/test/libcxx/containers/views/mdspan/nodiscard.verify.cpp (+4)
  • (modified) libcxx/test/std/containers/views/mdspan/CustomTestLayouts.h (+76)
  • (added) libcxx/test/std/containers/views/mdspan/mdspan/at.pass.cpp (+262)
  • (modified) libcxx/test/std/language.support/support.limits/support.limits.general/mdspan.version.compile.pass.cpp (+2-2)
  • (modified) libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp (+2-2)
  • (modified) libcxx/utils/generate_feature_test_macro_components.py (+1)
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 32911d0f64449..abe478b6a8d2b 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -484,7 +484,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_linalg``                                       *unimplemented*
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_mdspan``                                       ``202406L``
+    ``__cpp_lib_mdspan``                                       ``202506L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_not_fn``                                       ``202306L``
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index 29642fc53cac6..93843450b5cba 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|","22","`#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 9d3d35cd558a1..d32aa355c7305 100644
--- a/libcxx/include/__mdspan/mdspan.h
+++ b/libcxx/include/__mdspan/mdspan.h
@@ -40,6 +40,9 @@
 #include <__utility/integer_sequence.h>
 #include <array>
 #include <span>
+#if _LIBCPP_STD_VER >= 26
+#  include <stdexcept>
+#endif
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -214,6 +217,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 operator[](static_cast<index_type>(std::move(__indices))...);
+  }
+
+  template <class _OtherIndexType>
+    requires(is_convertible_v<const _OtherIndexType&, index_type> &&
+             is_nothrow_constructible_v<index_type, const _OtherIndexType&>)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr reference at(const array<_OtherIndexType, rank()>& __indices) const {
+    return [&]<size_t... _Idxs>(index_sequence<_Idxs...>) -> reference {
+      return at(static_cast<index_type>(__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(static_cast<index_type>(__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
@@ -270,6 +304,10 @@ class mdspan {
 
   template <class, class, class, class>
   friend class mdspan;
+
+#  if _LIBCPP_VERSION >= 26
+  [[noreturn]] _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 32468a128dc9a..f85353cc9d758 100644
--- a/libcxx/include/mdspan
+++ b/libcxx/include/mdspan
@@ -364,6 +364,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;
     [[nodiscard]] constexpr bool empty() const noexcept;
 
diff --git a/libcxx/include/version b/libcxx/include/version
index 1d2422e64ba0f..1049ff7188463 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -174,7 +174,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                                        202406L <mdspan>
+__cpp_lib_mdspan                                        202506L <mdspan>
                                                         202207L // C++23
 __cpp_lib_memory_resource                               201603L <memory_resource>
 __cpp_lib_move_iterator_concept                         202207L <iterator>
@@ -593,7 +593,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # endif
 // # define __cpp_lib_linalg                               202311L
 # undef  __cpp_lib_mdspan
-# define __cpp_lib_mdspan                               202406L
+# define __cpp_lib_mdspan                               202506L
 # undef  __cpp_lib_not_fn
 # define __cpp_lib_not_fn                               202306L
 # undef  __cpp_lib_optional
diff --git a/libcxx/test/libcxx/containers/views/mdspan/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/views/mdspan/nodiscard.verify.cpp
index d248656ec0ae5..b132c4a6da09b 100644
--- a/libcxx/test/libcxx/containers/views/mdspan/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/views/mdspan/nodiscard.verify.cpp
@@ -28,6 +28,10 @@ void test() {
   std::span sp{arr};
   mdsp[sp]; // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
 
+  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}}
+
   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/CustomTestLayouts.h b/libcxx/test/std/containers/views/mdspan/CustomTestLayouts.h
index 588a5e9774a55..4b33f11dc0086 100644
--- a/libcxx/test/std/containers/views/mdspan/CustomTestLayouts.h
+++ b/libcxx/test/std/containers/views/mdspan/CustomTestLayouts.h
@@ -335,4 +335,80 @@ class always_convertible_layout::mapping {
   index_type offset_{};
   index_type scaling_{};
 };
+
+struct SpyIndex {
+  int val;
+  constexpr SpyIndex(int v) : val(v) {}
+  constexpr operator int() const noexcept { return val; }
+};
+
+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   = typename extents_type::index_type;
+  using size_type    = typename extents_type::size_type;
+  using rank_type    = typename 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 s = 1;
+    for (rank_type i = r + 1; i < extents_type::rank(); i++)
+      s *= extents_.extent(i);
+    return s;
+  }
+
+  friend constexpr bool operator==(const mapping& lhs, const mapping& rhs) noexcept {
+    return lhs.extents_ == rhs.extents_;
+  }
+
+private:
+  extents_type extents_{};
+};
+
 #endif // TEST_STD_CONTAINERS_VIEWS_MDSPAN_CUSTOM_TEST_LAYOUTS_H
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..8fdb721e5ebe4
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/at.pass.cpp
@@ -0,0 +1,262 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++23
+
+// <mdspan>
+
+// 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;
+//
+// Constraints:
+//   * sizeof...(OtherIndexTypes) == extents_type::rank() is true,
+//   * (is_convertible_v<OtherIndexTypes, index_type> && ...) is true,
+//   * (is_nothrow_constructible_v<index_type, OtherIndexTypes> && ...) 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> // dynamic_extent
+#ifndef TEST_HAS_NO_EXCEPTIONS
+#  include <vector>
+#endif
+
+#include "test_macros.h"
+
+#include "../ConvertibleToIntegral.h"
+#include "../CustomTestLayouts.h"
+
+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) {
+    [[maybe_unused]] int* ptr_accessor = &(mds.accessor().access(mds.data_handle(), mds.mapping()(args...)));
+    std::array<typename MDS::index_type, MDS::rank()> args_arr{static_cast<typename MDS::index_type>(args)...};
+
+    // mdspan.at(indices...)
+    [[maybe_unused]] typename MDS::element_type* ptr_at = &mds.at(args...);
+    assert(ptr_at == ptr_accessor);
+
+    //  mdspan.at(array)
+    [[maybe_unused]] typename MDS::element_type* ptr_arr = &mds.at(args_arr);
+    assert(&mds.at(args_arr) == ptr_accessor);
+
+    // mdspan.at(span)
+    [[maybe_unused]] typename MDS::element_type* 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() {
+  constexpr size_t D = std::dynamic_extent;
+  test_iteration(construct_mapping(Layout(), std::extents<int>()));
+  test_iteration(construct_mapping(Layout(), std::extents<unsigned, D>(1)));
+  test_iteration(construct_mapping(Layout(), std::extents<unsigned, D>(7)));
+  test_iteration(construct_mapping(Layout(), std::extents<unsigned, 7>()));
+  test_iteration(construct_mapping(Layout(), std::extents<unsigned, 7, 8>()));
+  test_iteration(construct_mapping(Layout(), std::extents<signed char, D, D, D, D>(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::extents<int, D>(1))), 0));
+  static_assert(!check_at_constraints(std::mdspan(data, construct_mapping(Layout(), std::extents<int, D>(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::extents<int, D>(1))), IntType(0)));
+  static_assert(
+      !check_at_constraints(std::mdspan(data, construct_mapping(Layout(), std::extents<unsigned, D>(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::extents<unsigned char, D>(1))), IntType(0)));
+
+  // Check that mixed integrals work: note the second one tests that mdspan casts: layout_wrapping_integral does not accept IntType
+  static_assert(check_at_constraints(
+      std::mdspan(data, construct_mapping(Layout(), std::extents<unsigned char, D, D>(1, 1))), int(0), size_t(0)));
+  static_assert(check_at_constraints(
+      std::mdspan(data, construct_mapping(Layout(), std::extents<int, D, D>(1, 1))), 0uz, IntType(0)));
+
+  constexpr bool t = true;
+  constexpr bool o = false;
+  static_assert(!check_at_constraints(
+      std::mdspan(data, construct_mapping(Layout(), std::extents<int, D, D>(1, 1))), 0uz, IntConfig<o, o, t, t>(0)));
+  static_assert(check_at_constraints(
+      std::mdspan(data, construct_mapping(Layout(), std::extents<int, D, D>(1, 1))), 0uz, IntConfig<o, t, t, t>(0)));
+  static_assert(check_at_constraints(
+      std::mdspan(data, construct_mapping(Layout(), std::extents<int, D, D>(1, 1))), 0uz, IntConfig<o, t, o, t>(0)));
+  static_assert(!check_at_constraints(
+      std::mdspan(data, construct_mapping(Layout(), std::extents<int, D, D>(1, 1))), 0uz, IntConfig<t, o, o, t>(0)));
+  static_assert(check_at_constraints(
+      std::mdspan(data, construct_mapping(Layout(), std::extents<int, D, D>(1, 1))), 0uz, IntConfig<t, o, t, o>(0)));
+  static_assert(check_at_constraints(
+      std::mdspan(data, construct_mapping(Layout(), std::extents<int, D, D>(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::extents<int, D>(1))), std::array{IntConfig<o, o, t, t>(0)}));
+    static_assert(!check_at_constraints(
+        std::mdspan(data, construct_mapping(Layout(), std::extents<int, D>(1))), std::array{IntConfig<o, t, t, t>(0)}));
+    static_assert(!check_at_constraints(
+        std::mdspan(data, construct_mapping(Layout(), std::extents<int, D>(1))), std::array{IntConfig<t, o, o, t>(0)}));
+    static_assert(!check_at_constraints(
+        std::mdspan(data, construct_mapping(Layout(), std::extents<int, D>(1))), std::array{IntConfig<t, t, o, t>(0)}));
+    static_assert(check_at_constraints(
+        std::mdspan(data, construct_mapping(Layout(), std::extents<int, D>(1))), std::array{IntConfig<t, o, t, o>(0)}));
+    static_assert(check_at_constraints(
+        std::mdspan(data, construct_mapping(Layout(), std::extents<int, D>(1))), std::array{IntConfig<t, t, t, t>(0)}));
+
+    {
+      [[maybe_unused]] std::array idx{IntConfig<o, o, t, t>(0)};
+      assert(!check_at_constraints(
+          std::mdspan(data, construct_mapping(Layout(), std::extents<int, D>(1))), std::span(idx)));
+    }
+    {
+      [[maybe_unused]] std::array idx{IntConfig<o, t, t, t>(0)};
+      assert(!check_at_constraints(
+          std::mdspan(data, construct_mapping(Layout(), std::extents<int, D>(1))), std::span(idx)));
+    }
+    {
+      [[maybe_unused]] std::array idx{IntConfig<t, o, o, t>(0)};
+      assert(!check_at_constraints(
+          std::mdspan(data, construct_mapping(Layout(), std::extents<int, D>(1))), std::span(idx)));
+    }
+    {
+      [[maybe_unused]] std::array idx{IntConfig<t, t, o, t>(0)};
+      assert(!check_at_constraints(
+          std::mdspan(data, construct_mapping(Layout(), std::extents<int, D>(1))), std::span(idx)));
+    }
+    {
+      [[maybe_unused]] std::array idx{IntConfig<t, o, t, o>(0)};
+      assert(check_at_constraints(
+          std::mdspan(data, construct_mapping(Layout(), std::extents<int, D>(1))), std::span(idx)));
+    }
+    {
+      [[maybe_unused]] std::array idx{IntConfig<t, t, t, t>(0)};
+      assert(check_at_constraints(
+          std::mdspan(data, construct_mapping(Layout(), std::extents<int, D>(1))), std::span(idx)));
+    }
+  }
+}
+
+constexpr bool test...
[truncated]

@eiytoq eiytoq force-pushed the libcxx-p3383-mdspan-at branch 2 times, most recently from c52f44c to 170f2fe Compare January 9, 2026 21:27

@H-G-Hristov H-G-Hristov left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for working on this. I didn't do a real review, just a few drive-by comments.

Comment thread libcxx/include/__mdspan/mdspan.h Outdated
Comment thread libcxx/include/__mdspan/mdspan.h Outdated
Comment thread libcxx/test/std/containers/views/mdspan/mdspan/at.pass.cpp Outdated
Comment thread libcxx/test/std/containers/views/mdspan/mdspan/at.pass.cpp Outdated
Comment thread libcxx/test/std/containers/views/mdspan/mdspan/at.pass.cpp Outdated
Comment thread libcxx/test/std/containers/views/mdspan/mdspan/at.pass.cpp Outdated
Comment thread libcxx/test/std/containers/views/mdspan/mdspan/at.pass.cpp Outdated
Comment thread libcxx/test/std/containers/views/mdspan/mdspan/at.pass.cpp Outdated
Comment thread libcxx/test/std/containers/views/mdspan/mdspan/at.pass.cpp Outdated
Comment thread libcxx/test/std/containers/views/mdspan/CustomTestLayouts.h Outdated
@eiytoq eiytoq force-pushed the libcxx-p3383-mdspan-at branch from 170f2fe to 69f4f67 Compare January 10, 2026 13:55
@eiytoq

eiytoq commented Jan 10, 2026

Copy link
Copy Markdown
Contributor Author

@H-G-Hristov Thank you for your detailed review. I have addressed all the comments.

Comment thread libcxx/test/std/containers/views/mdspan/mdspan/at.pass.cpp Outdated
@eiytoq eiytoq force-pushed the libcxx-p3383-mdspan-at branch from 69f4f67 to cc0c26c Compare January 10, 2026 21:46
Comment thread libcxx/test/std/containers/views/mdspan/mdspan/at.pass.cpp Outdated
Comment thread libcxx/include/__mdspan/mdspan.h Outdated
@eiytoq eiytoq force-pushed the libcxx-p3383-mdspan-at branch 2 times, most recently from 31553a4 to 2661c40 Compare January 16, 2026 18:15
@eiytoq eiytoq requested review from Zingam and philnik777 January 17, 2026 21:41
Comment thread libcxx/utils/generate_feature_test_macro_components.py Outdated
@eiytoq eiytoq force-pushed the libcxx-p3383-mdspan-at branch from 473720c to f7c38d4 Compare January 22, 2026 09:07
@eiytoq eiytoq force-pushed the libcxx-p3383-mdspan-at branch from f7c38d4 to e4730e5 Compare February 5, 2026 06:52
@eiytoq eiytoq force-pushed the libcxx-p3383-mdspan-at branch from e4730e5 to 6b71ec0 Compare February 24, 2026 12:55
@eiytoq

eiytoq commented Mar 6, 2026

Copy link
Copy Markdown
Contributor Author

Ping~

@eiytoq eiytoq force-pushed the libcxx-p3383-mdspan-at branch from 6b71ec0 to 31fc347 Compare May 5, 2026 10:44
@eiytoq

eiytoq commented May 5, 2026

Copy link
Copy Markdown
Contributor Author

Use index-cast and as_const in mdspan::at and improve test coverage.

@eiytoq eiytoq changed the title [libc++][mdspan] P3383R3: mdspan.at() [libc++][mdspan] P3383R3: mdspan.at() May 5, 2026
@eiytoq

eiytoq commented May 19, 2026

Copy link
Copy Markdown
Contributor Author

Ping.

Comment thread libcxx/utils/generate_feature_test_macro_components.py Outdated
Comment on lines +208 to +209
constexpr bool t = true;
constexpr bool o = false;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No change requested for this.

Current t and o are quite unreadable. Maybe it would be better to refactor IntConfig with scoped enums (e.g. the enumerators could be (const_)conv::{disabled,may_throw,no_throw}) in another PR.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of the test code was migrated from index_operator.pass.cpp, and I plan to refactor these tests in the future.

Comment thread libcxx/include/__mdspan/mdspan.h Outdated
Comment thread libcxx/include/__mdspan/mdspan.h Outdated
@eiytoq eiytoq requested a review from frederick-vs-ja May 19, 2026 13:25
eiytoq and others added 4 commits May 19, 2026 21:31
Co-authored-by: A. Jiang <de34@live.cn>
Co-authored-by: A. Jiang <de34@live.cn>
@eiytoq eiytoq force-pushed the libcxx-p3383-mdspan-at branch from f3dd8fc to 23a4220 Compare May 19, 2026 13:31
Comment thread libcxx/include/__mdspan/mdspan.h
Co-authored-by: A. Jiang <de34@live.cn>
Comment thread libcxx/test/libcxx/containers/views/mdspan/mdspan/assert.at.pass.cpp Outdated
Comment thread libcxx/test/libcxx/containers/views/mdspan/mdspan/assert.at.pass.cpp Outdated
eiytoq and others added 2 commits May 22, 2026 10:33
Co-authored-by: A. Jiang <de34@live.cn>

@frederick-vs-ja frederick-vs-ja left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. CI failures are certainly unrelated. Let's merge this.

@eiytoq eiytoq merged commit 8cc944c into llvm:main May 22, 2026
133 of 137 checks passed
@eiytoq eiytoq deleted the libcxx-p3383-mdspan-at branch May 22, 2026 11:54
@thurstond

thurstond commented May 23, 2026

Copy link
Copy Markdown
Contributor

Some buildbots (e.g., Arm64 ASan: https://lab.llvm.org/staging/#/builders/42/builds/4697/steps/10/logs/stdio; Arm64 MSan: https://lab.llvm.org/staging/#/builders/7/builds/1687) are failing after this commit landed. Could you please take a look?

FAIL: llvm-libc++-shared.cfg.in :: libcxx/containers/views/mdspan/mdspan/assert.at.pass.cpp (2283 of 11439)
******************** TEST 'llvm-libc++-shared.cfg.in :: libcxx/containers/views/mdspan/mdspan/assert.at.pass.cpp' FAILED ********************
Exit Code: 250
Command Output (stdout):
--
# COMPILED WITH
/home/b/sanitizer-aarch64-linux-bootstrap-asan/build/llvm_build0/bin/clang++ /home/b/sanitizer-aarch64-linux-bootstrap-asan/build/llvm-project/libcxx/test/libcxx/containers/views/mdspan/mdspan/assert.at.pass.cpp -pthread --target=aarch64-unknown-linux-gnu -g -fno-omit-frame-pointer -fsanitize=address -nostdinc++ -I /home/b/sanitizer-aarch64-linux-bootstrap-asan/build/libcxx_build_asan/libcxx/test-suite-install/include/c++/v1 -I /home/b/sanitizer-aarch64-linux-bootstrap-asan/build/libcxx_build_asan/libcxx/test-suite-install/include/c++/v1 -I /home/b/sanitizer-aarch64-linux-bootstrap-asan/build/llvm-project/libcxx/test/support -std=c++26 -Werror -Wall -Wctad-maybe-unsupported -Wextra -Wshadow -Wundef -Wunused-template -Wno-unused-command-line-argument -Wno-attributes -Wno-pessimizing-move -Wno-noexcept-type -Wno-atomic-alignment -Wno-reserved-module-identifier -Wdeprecated-copy -Wdeprecated-copy-dtor -Wshift-negative-value -Wno-user-defined-literals -Wno-tautological-compare -Wsign-compare -Wunused-variable -Wunused-parameter -Wunreachable-code -Wno-unused-local-typedef -Wno-local-type-template-args -Wno-c++11-extensions -Wno-unknown-pragmas -Wno-pass-failed -Wno-mismatched-new-delete -Wno-redundant-move -Wno-self-move -Wno-nullability-completeness -flax-vector-conversions=none -D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER -D_LIBCPP_ENABLE_EXPERIMENTAL -Wuser-defined-warnings  -lc++experimental -nostdlib++ -L /home/b/sanitizer-aarch64-linux-bootstrap-asan/build/libcxx_build_asan/libcxx/test-suite-install/lib -Wl,-rpath,/home/b/sanitizer-aarch64-linux-bootstrap-asan/build/libcxx_build_asan/libcxx/test-suite-install/lib -lc++ -latomic -o /home/b/sanitizer-aarch64-linux-bootstrap-asan/build/libcxx_build_asan/libcxx/test/libcxx/containers/views/mdspan/mdspan/Output/assert.at.pass.cpp.dir/t.tmp.exe
# executed command: /home/b/sanitizer-aarch64-linux-bootstrap-asan/build/llvm_build0/bin/clang++ /home/b/sanitizer-aarch64-linux-bootstrap-asan/build/llvm-project/libcxx/test/libcxx/containers/views/mdspan/mdspan/assert.at.pass.cpp -pthread --target=aarch64-unknown-linux-gnu -g -fno-omit-frame-pointer -fsanitize=address -nostdinc++ -I /home/b/sanitizer-aarch64-linux-bootstrap-asan/build/libcxx_build_asan/libcxx/test-suite-install/include/c++/v1 -I /home/b/sanitizer-aarch64-linux-bootstrap-asan/build/libcxx_build_asan/libcxx/test-suite-install/include/c++/v1 -I /home/b/sanitizer-aarch64-linux-bootstrap-asan/build/llvm-project/libcxx/test/support -std=c++26 -Werror -Wall -Wctad-maybe-unsupported -Wextra -Wshadow -Wundef -Wunused-template -Wno-unused-command-line-argument -Wno-attributes -Wno-pessimizing-move -Wno-noexcept-type -Wno-atomic-alignment -Wno-reserved-module-identifier -Wdeprecated-copy -Wdeprecated-copy-dtor -Wshift-negative-value -Wno-user-defined-literals -Wno-tautological-compare -Wsign-compare -Wunused-variable -Wunused-parameter -Wunreachable-code -Wno-unused-local-typedef -Wno-local-type-template-args -Wno-c++11-extensions -Wno-unknown-pragmas -Wno-pass-failed -Wno-mismatched-new-delete -Wno-redundant-move -Wno-self-move -Wno-nullability-completeness -flax-vector-conversions=none -D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER -D_LIBCPP_ENABLE_EXPERIMENTAL -Wuser-defined-warnings -lc++experimental -nostdlib++ -L /home/b/sanitizer-aarch64-linux-bootstrap-asan/build/libcxx_build_asan/libcxx/test-suite-install/lib -Wl,-rpath,/home/b/sanitizer-aarch64-linux-bootstrap-asan/build/libcxx_build_asan/libcxx/test-suite-install/lib -lc++ -latomic -o /home/b/sanitizer-aarch64-linux-bootstrap-asan/build/libcxx_build_asan/libcxx/test/libcxx/containers/views/mdspan/mdspan/Output/assert.at.pass.cpp.dir/t.tmp.exe
# note: command had no output on stdout or stderr
# EXECUTED AS
/home/b/sanitizer-aarch64-linux-bootstrap-asan/build/llvm_venv/bin/python /home/b/sanitizer-aarch64-linux-bootstrap-asan/build/llvm-project/libcxx/utils/run.py --execdir /home/b/sanitizer-aarch64-linux-bootstrap-asan/build/libcxx_build_asan/libcxx/test/libcxx/containers/views/mdspan/mdspan/Output/assert.at.pass.cpp.dir --  /home/b/sanitizer-aarch64-linux-bootstrap-asan/build/libcxx_build_asan/libcxx/test/libcxx/containers/views/mdspan/mdspan/Output/assert.at.pass.cpp.dir/t.tmp.exe
# executed command: /home/b/sanitizer-aarch64-linux-bootstrap-asan/build/llvm_venv/bin/python /home/b/sanitizer-aarch64-linux-bootstrap-asan/build/llvm-project/libcxx/utils/run.py --execdir /home/b/sanitizer-aarch64-linux-bootstrap-asan/build/libcxx_build_asan/libcxx/test/libcxx/containers/views/mdspan/mdspan/Output/assert.at.pass.cpp.dir -- /home/b/sanitizer-aarch64-linux-bootstrap-asan/build/libcxx_build_asan/libcxx/test/libcxx/containers/views/mdspan/mdspan/Output/assert.at.pass.cpp.dir/t.tmp.exe
# .---command stderr------------
# | libc++abi: terminating due to uncaught exception of type std::out_of_range: mdspan
# `-----------------------------
# error: command failed with exit status: 250

@eiytoq

eiytoq commented May 23, 2026

Copy link
Copy Markdown
Contributor Author

@thurstond Thanks for the heads-up.

I don’t have access to a native AArch64 machine, so I can’t reproduce this locally right now. I took another look and don’t see an obvious issue yet.

I can send a patch to XFAIL this while we figure out the problem.

@frederick-vs-ja

Copy link
Copy Markdown
Contributor

@eiytoq @thurstond I'm trying to fix this. It seems that // UNSUPPORTED: no-exceptions in the libcxx/std/libcxx subdirectory sometimes doesn't work with some builds. I'm trying to merge two test files and use TEST_HAS_NO_EXCEPTIONS for conditional compilation.

@thurstond

Copy link
Copy Markdown
Contributor

Thanks @eiytoq and @frederick-vs-ja for taking a look! Bots are green now after @frederick-vs-ja's fix #199330.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

P3383R3: mdspan.at()

7 participants