[libcxx-commits] [libcxx] [libc++][mdspan] LWG3974: `mdspan::operator[]` should not copy `OtherIndexTypes` (PR #195814)

via libcxx-commits libcxx-commits at lists.llvm.org
Thu May 7 03:33:57 PDT 2026


https://github.com/eiytoq updated https://github.com/llvm/llvm-project/pull/195814

>From 0dfdb46ac0298a80b2d84e1c4059641ae8b502b4 Mon Sep 17 00:00:00 2001
From: eiytoq <eiytoq at outlook.com>
Date: Tue, 5 May 2026 17:19:34 +0800
Subject: [PATCH 1/2] [libc++][mdspan] LWG3974: mdspan::operator[] should not
 copy OtherIndexTypes

---
 libcxx/docs/Status/Cxx2cIssues.csv            |  2 +-
 libcxx/include/__mdspan/mdspan.h              |  4 ++--
 .../mdspan/mdspan/index_operator.pass.cpp     | 21 ++++++++++++++++++-
 3 files changed, 23 insertions(+), 4 deletions(-)

diff --git a/libcxx/docs/Status/Cxx2cIssues.csv b/libcxx/docs/Status/Cxx2cIssues.csv
index d7f75380cb93e..45fba58cb3f91 100644
--- a/libcxx/docs/Status/Cxx2cIssues.csv
+++ b/libcxx/docs/Status/Cxx2cIssues.csv
@@ -35,7 +35,7 @@
 "`LWG3965 <https://wg21.link/LWG3965>`__","Incorrect example in [format.string.escaped] p3 for formatting of combining characters","2023-11 (Kona)","|Complete|","19","`#105305 <https://github.com/llvm/llvm-project/issues/105305>`__",""
 "`LWG3970 <https://wg21.link/LWG3970>`__","[mdspan.syn] Missing definition of ``full_extent_t`` and ``full_extent``","2023-11 (Kona)","","","`#105306 <https://github.com/llvm/llvm-project/issues/105306>`__",""
 "`LWG3973 <https://wg21.link/LWG3973>`__","Monadic operations should be ADL-proof","2023-11 (Kona)","","","`#105307 <https://github.com/llvm/llvm-project/issues/105307>`__",""
-"`LWG3974 <https://wg21.link/LWG3974>`__","``mdspan::operator[]`` should not copy ``OtherIndexTypes``","2023-11 (Kona)","","","`#105308 <https://github.com/llvm/llvm-project/issues/105308>`__",""
+"`LWG3974 <https://wg21.link/LWG3974>`__","``mdspan::operator[]`` should not copy ``OtherIndexTypes``","2023-11 (Kona)","|Complete|","23","`#105308 <https://github.com/llvm/llvm-project/issues/105308>`__",""
 "`LWG3987 <https://wg21.link/LWG3987>`__","Including ``<flat_foo>`` doesn't provide ``std::begin``/``end``","2023-11 (Kona)","|Complete|","","`#105309 <https://github.com/llvm/llvm-project/issues/105309>`__",""
 "`LWG3990 <https://wg21.link/LWG3990>`__","Program-defined specializations of ``std::tuple`` and ``std::variant`` can't be properly supported","2023-11 (Kona)","|Complete|","21","`#105310 <https://github.com/llvm/llvm-project/issues/105310>`__",""
 "`LWG4001 <https://wg21.link/LWG4001>`__","``iota_view`` should provide ``empty``","2023-11 (Kona)","|Complete|","19","`#105311 <https://github.com/llvm/llvm-project/issues/105311>`__",""
diff --git a/libcxx/include/__mdspan/mdspan.h b/libcxx/include/__mdspan/mdspan.h
index 7f577953ee7ec..7f71b0750a78d 100644
--- a/libcxx/include/__mdspan/mdspan.h
+++ b/libcxx/include/__mdspan/mdspan.h
@@ -204,7 +204,7 @@ class mdspan {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr reference
   operator[](const array< _OtherIndexType, rank()>& __indices) const {
     return __acc_.access(__ptr_, [&]<size_t... _Idxs>(index_sequence<_Idxs...>) {
-      return __map_(__indices[_Idxs]...);
+      return __map_(extents_type::__index_cast(__indices[_Idxs])...);
     }(make_index_sequence<rank()>()));
   }
 
@@ -213,7 +213,7 @@ class mdspan {
              is_nothrow_constructible_v<index_type, const _OtherIndexType&>)
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr reference operator[](span<_OtherIndexType, rank()> __indices) const {
     return __acc_.access(__ptr_, [&]<size_t... _Idxs>(index_sequence<_Idxs...>) {
-      return __map_(__indices[_Idxs]...);
+      return __map_(extents_type::__index_cast(__indices[_Idxs])...);
     }(make_index_sequence<rank()>()));
   }
 
diff --git a/libcxx/test/std/containers/views/mdspan/mdspan/index_operator.pass.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/index_operator.pass.cpp
index 3f20bc886d106..bcf8d3628dbb0 100644
--- a/libcxx/test/std/containers/views/mdspan/mdspan/index_operator.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/index_operator.pass.cpp
@@ -196,9 +196,27 @@ constexpr void test_layout_large() {
   test_iteration(construct_mapping(Layout(), std::extents<int64_t, D, 4, 1, D>(3, 6)));
 }
 
+struct Index {
+  Index()             = default;
+  Index(const Index&) = delete;
+  constexpr operator int() const noexcept { return 0; }
+};
+
 // mdspan::operator[] casts to index_type before calling mapping
 // mapping requirements only require the index operator to mixed integer types not anything convertible to index_type
-constexpr void test_index_cast_happens() {}
+constexpr void test_index_cast_happens() {
+  int data[1]{};
+  std::mdspan m(data, std::extents<int, 1>{1});
+
+  // Index i;
+  std::array<Index, 1> a{};
+  std::span s(a);
+
+  // LWG3995: Issue with custom index conversion in <mdspan>
+  // TEST_IGNORE_NODISCARD m[i];
+  TEST_IGNORE_NODISCARD m[a];
+  TEST_IGNORE_NODISCARD m[s];
+}
 
 struct RValueInt {
   constexpr operator int() && noexcept { return 0; }
@@ -213,6 +231,7 @@ constexpr bool test() {
   std::mdspan m(data, std::extents<int, 1>{1});
   TEST_IGNORE_NODISCARD m[RValueInt{}];
 
+  test_index_cast_happens();
   return true;
 }
 

>From 8343d665eaa8555762d5e3a8f802431c3f719f37 Mon Sep 17 00:00:00 2001
From: eiytoq <eiytoq at outlook.com>
Date: Thu, 7 May 2026 18:33:38 +0800
Subject: [PATCH 2/2] Improve test coverage

---
 .../mdspan/mdspan/index_operator.pass.cpp     | 30 ++++++++++---------
 1 file changed, 16 insertions(+), 14 deletions(-)

diff --git a/libcxx/test/std/containers/views/mdspan/mdspan/index_operator.pass.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/index_operator.pass.cpp
index bcf8d3628dbb0..656d2475d2588 100644
--- a/libcxx/test/std/containers/views/mdspan/mdspan/index_operator.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/index_operator.pass.cpp
@@ -196,26 +196,28 @@ constexpr void test_layout_large() {
   test_iteration(construct_mapping(Layout(), std::extents<int64_t, D, 4, 1, D>(3, 6)));
 }
 
-struct Index {
-  Index()             = default;
-  Index(const Index&) = delete;
-  constexpr operator int() const noexcept { return 0; }
+struct NoCopyIndex {
+  int val = 0;
+  constexpr NoCopyIndex(int v) : val(v) {}
+  constexpr NoCopyIndex(const NoCopyIndex&) = delete;
+  constexpr operator int() const noexcept { return val; }
 };
 
 // mdspan::operator[] casts to index_type before calling mapping
 // mapping requirements only require the index operator to mixed integer types not anything convertible to index_type
-constexpr void test_index_cast_happens() {
-  int data[1]{};
-  std::mdspan m(data, std::extents<int, 1>{1});
+constexpr void test_index_cast() {
+  std::array data{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+  const std::mdspan m{data.data(), std::dextents<int, 1>{10}};
+
+  std::array indices{NoCopyIndex{3}};
 
-  // Index i;
-  std::array<Index, 1> a{};
-  std::span s(a);
+  assert(&m[NoCopyIndex{3}] == &data[3]);
+  assert(&m[indices] == &data[3]);
+  assert(&m[std::span{indices}] == &data[3]);
 
   // LWG3995: Issue with custom index conversion in <mdspan>
-  // TEST_IGNORE_NODISCARD m[i];
-  TEST_IGNORE_NODISCARD m[a];
-  TEST_IGNORE_NODISCARD m[s];
+  // NoCopyIndex index(3);
+  // assert(&m[index] == &data[3]);
 }
 
 struct RValueInt {
@@ -231,7 +233,7 @@ constexpr bool test() {
   std::mdspan m(data, std::extents<int, 1>{1});
   TEST_IGNORE_NODISCARD m[RValueInt{}];
 
-  test_index_cast_happens();
+  test_index_cast();
   return true;
 }
 



More information about the libcxx-commits mailing list