[libcxx-commits] [libcxx] [libc++] LWG4266: `layout_stride::mapping` should treat empty mappings as exhaustive (PR #191629)

via libcxx-commits libcxx-commits at lists.llvm.org
Sat Apr 11 06:49:00 PDT 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: eiytoq (eiytoq)

<details>
<summary>Changes</summary>

Fixes: #<!-- -->171325

---
Full diff: https://github.com/llvm/llvm-project/pull/191629.diff


5 Files Affected:

- (modified) libcxx/docs/Status/Cxx2cIssues.csv (+1-1) 
- (modified) libcxx/include/__mdspan/layout_stride.h (+14-22) 
- (modified) libcxx/include/mdspan (+1-1) 
- (modified) libcxx/test/std/containers/views/mdspan/layout_stride/is_exhaustive_corner_case.pass.cpp (+5-5) 
- (modified) libcxx/test/std/containers/views/mdspan/layout_stride/properties.pass.cpp (+13-6) 


``````````diff
diff --git a/libcxx/docs/Status/Cxx2cIssues.csv b/libcxx/docs/Status/Cxx2cIssues.csv
index e17992f899b3b..15d29ff173149 100644
--- a/libcxx/docs/Status/Cxx2cIssues.csv
+++ b/libcxx/docs/Status/Cxx2cIssues.csv
@@ -165,7 +165,7 @@
 "`LWG4257 <https://wg21.link/LWG4257>`__","Stream insertion for ``chrono::local_time`` should be constrained","2025-11 (Kona)","","","`#171322 <https://github.com/llvm/llvm-project/issues/171322>`__",""
 "`LWG4260 <https://wg21.link/LWG4260>`__","Query objects must be default constructible","2025-11 (Kona)","","","`#171323 <https://github.com/llvm/llvm-project/issues/171323>`__",""
 "`LWG4265 <https://wg21.link/LWG4265>`__","``std::midpoint`` should not accept ``const bool``","2025-11 (Kona)","|Complete|","22","`#171324 <https://github.com/llvm/llvm-project/issues/171324>`__",""
-"`LWG4266 <https://wg21.link/LWG4266>`__","``layout_stride::mapping`` should treat empty mappings as exhaustive","2025-11 (Kona)","","","`#171325 <https://github.com/llvm/llvm-project/issues/171325>`__",""
+"`LWG4266 <https://wg21.link/LWG4266>`__","``layout_stride::mapping`` should treat empty mappings as exhaustive","2025-11 (Kona)","|Complete|","23","`#171325 <https://github.com/llvm/llvm-project/issues/171325>`__",""
 "`LWG4269 <https://wg21.link/LWG4269>`__","``unique_copy`` passes arguments to its predicate backwards","2025-11 (Kona)","","","`#171326 <https://github.com/llvm/llvm-project/issues/171326>`__",""
 "`LWG4272 <https://wg21.link/LWG4272>`__","For ``rank == 0``, ``layout_stride`` is atypically convertible","2025-11 (Kona)","","","`#171327 <https://github.com/llvm/llvm-project/issues/171327>`__",""
 "`LWG4274 <https://wg21.link/LWG4274>`__","The ``chrono::hh_mm_ss`` constructor is ill-formed for unsigned durations","2025-11 (Kona)","","","`#171328 <https://github.com/llvm/llvm-project/issues/171328>`__",""
diff --git a/libcxx/include/__mdspan/layout_stride.h b/libcxx/include/__mdspan/layout_stride.h
index eb22475756fde..1d586f5b1b0ef 100644
--- a/libcxx/include/__mdspan/layout_stride.h
+++ b/libcxx/include/__mdspan/layout_stride.h
@@ -296,7 +296,14 @@ class layout_stride::mapping {
   }
 
   _LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_unique() noexcept { return true; }
-  _LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_exhaustive() noexcept { return false; }
+  _LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_exhaustive() noexcept {
+    if constexpr (__rank_ == 0)
+      return true;
+    for (size_t __r = 0; __r < __rank_; ++__r)
+      if (extents_type::static_extent(__r) == 0)
+        return true;
+    return false;
+  }
   _LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_strided() noexcept { return true; }
 
   _LIBCPP_HIDE_FROM_ABI static constexpr bool is_unique() noexcept { return true; }
@@ -307,27 +314,12 @@ class layout_stride::mapping {
   _LIBCPP_HIDE_FROM_ABI constexpr bool is_exhaustive() const noexcept {
     if constexpr (__rank_ == 0)
       return true;
-    else {
-      index_type __span_size = required_span_size();
-      if (__span_size == static_cast<index_type>(0)) {
-        if constexpr (__rank_ == 1)
-          return __strides_[0] == 1;
-        else {
-          rank_type __r_largest = 0;
-          for (rank_type __r = 1; __r < __rank_; __r++)
-            if (__strides_[__r] > __strides_[__r_largest])
-              __r_largest = __r;
-          for (rank_type __r = 0; __r < __rank_; __r++)
-            if (__extents_.extent(__r) == 0 && __r != __r_largest)
-              return false;
-          return true;
-        }
-      } else {
-        return required_span_size() == [&]<size_t... _Pos>(index_sequence<_Pos...>) {
-          return (__extents_.extent(_Pos) * ... * static_cast<index_type>(1));
-        }(make_index_sequence<__rank_>());
-      }
-    }
+    index_type __span_size = required_span_size();
+    if (__span_size == static_cast<index_type>(0))
+      return true;
+    return __span_size == [&]<size_t... _Pos>(index_sequence<_Pos...>) {
+      return (__extents_.extent(_Pos) * ... * static_cast<index_type>(1));
+    }(make_index_sequence<__rank_>());
   }
   _LIBCPP_HIDE_FROM_ABI static constexpr bool is_strided() noexcept { return true; }
 
diff --git a/libcxx/include/mdspan b/libcxx/include/mdspan
index 32468a128dc9a..6b0aefd05e2f0 100644
--- a/libcxx/include/mdspan
+++ b/libcxx/include/mdspan
@@ -237,7 +237,7 @@ namespace std {
       constexpr index_type operator()(Indices...) const noexcept;
 
     static constexpr bool is_always_unique() noexcept { return true; }
-    static constexpr bool is_always_exhaustive() noexcept { return false; }
+    static constexpr bool is_always_exhaustive() noexcept;
     static constexpr bool is_always_strided() noexcept { return true; }
 
     static constexpr bool is_unique() noexcept { return true; }
diff --git a/libcxx/test/std/containers/views/mdspan/layout_stride/is_exhaustive_corner_case.pass.cpp b/libcxx/test/std/containers/views/mdspan/layout_stride/is_exhaustive_corner_case.pass.cpp
index 900adb42096a7..c139d4241ed3b 100644
--- a/libcxx/test/std/containers/views/mdspan/layout_stride/is_exhaustive_corner_case.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/layout_stride/is_exhaustive_corner_case.pass.cpp
@@ -13,7 +13,7 @@
 // constexpr bool is_exhaustive() const noexcept;
 //
 // Returns:
-//   - true if rank_ is 0.
+//   - true if rank_ or the size of the multidimensional index space m.extents() is 0.
 //   - Otherwise, true if there is a permutation P of the integers in the range [0, rank_) such that
 //     stride(p0) equals 1, and stride(pi) equals stride(pi_1) * extents().extent(pi_1) for i in the
 //     range [1, rank_), where pi is the ith element of P.
@@ -35,13 +35,13 @@ test_layout_mapping_stride(E ext, std::array<typename E::index_type, E::rank()>
 constexpr bool test() {
   constexpr size_t D = std::dynamic_extent;
   test_layout_mapping_stride(std::extents<int, 0>(), std::array<int, 1>{1}, true);
-  test_layout_mapping_stride(std::extents<unsigned, D>(0), std::array<unsigned, 1>{3}, false);
+  test_layout_mapping_stride(std::extents<unsigned, D>(0), std::array<unsigned, 1>{3}, true);
   test_layout_mapping_stride(std::extents<int, 0, 3>(), std::array<int, 2>{6, 2}, true);
-  test_layout_mapping_stride(std::extents<int, D, D>(3, 0), std::array<int, 2>{6, 2}, false);
-  test_layout_mapping_stride(std::extents<int, D, D>(0, 0), std::array<int, 2>{6, 2}, false);
+  test_layout_mapping_stride(std::extents<int, D, D>(3, 0), std::array<int, 2>{6, 2}, true);
+  test_layout_mapping_stride(std::extents<int, D, D>(0, 0), std::array<int, 2>{6, 2}, true);
   test_layout_mapping_stride(
       std::extents<unsigned, D, D, D, D>(3, 3, 0, 3), std::array<unsigned, 4>{3, 1, 27, 9}, true);
-  test_layout_mapping_stride(std::extents<int, D, D, D, D>(0, 3, 3, 3), std::array<int, 4>{3, 1, 27, 9}, false);
+  test_layout_mapping_stride(std::extents<int, D, D, D, D>(0, 3, 3, 3), std::array<int, 4>{3, 1, 27, 9}, true);
   return true;
 }
 
diff --git a/libcxx/test/std/containers/views/mdspan/layout_stride/properties.pass.cpp b/libcxx/test/std/containers/views/mdspan/layout_stride/properties.pass.cpp
index 21ce56fcc65f1..c3f472e790ba5 100644
--- a/libcxx/test/std/containers/views/mdspan/layout_stride/properties.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/layout_stride/properties.pass.cpp
@@ -16,11 +16,11 @@
 //
 //     ...
 //     static constexpr bool is_always_unique() noexcept { return true; }
-//     static constexpr bool is_always_exhaustive() noexcept { return false; }
+//     static constexpr bool is_always_exhaustive() noexcept;
 //     static constexpr bool is_always_strided() noexcept { return true; }
 //
 //     static constexpr bool is_unique() noexcept { return true; }
-//     static constexpr bool is_exhaustive() noexcept;
+//     constexpr bool is_exhaustive() noexcept;
 //     static constexpr bool is_strided() noexcept { return true; }
 //     ...
 //   };
@@ -29,6 +29,12 @@
 //
 // layout_stride::mapping<E> is a trivially copyable type that models regular for each E.
 //
+// static constexpr bool is_always_exhaustive() noexcept;
+//
+// Returns:
+//   - true if rank_ is 0 or if there is a rank index r of extents() such that
+//     extents_type::static_extent(r) is 0, otherwise false.
+//
 // constexpr bool is_exhaustive() const noexcept;
 //
 // Returns:
@@ -48,8 +54,8 @@
 #include "test_macros.h"
 
 template <class E>
-constexpr void
-test_layout_mapping_stride(E ext, std::array<typename E::index_type, E::rank()> strides, bool exhaustive) {
+constexpr void test_layout_mapping_stride(
+    E ext, std::array<typename E::index_type, E::rank()> strides, bool exhaustive, bool always_exhaustive = false) {
   using M = std::layout_stride::mapping<E>;
   M m(ext, strides);
   const M c_m = m;
@@ -62,7 +68,7 @@ test_layout_mapping_stride(E ext, std::array<typename E::index_type, E::rank()>
   assert(c_m.is_exhaustive() == exhaustive);
   assert(M::is_strided() == true);
   assert(M::is_always_unique() == true);
-  assert(M::is_always_exhaustive() == false);
+  assert(M::is_always_exhaustive() == always_exhaustive);
   assert(M::is_always_strided() == true);
 
   ASSERT_NOEXCEPT(m.strides());
@@ -103,7 +109,8 @@ test_layout_mapping_stride(E ext, std::array<typename E::index_type, E::rank()>
 
 constexpr bool test() {
   constexpr size_t D = std::dynamic_extent;
-  test_layout_mapping_stride(std::extents<int>(), std::array<int, 0>{}, true);
+  test_layout_mapping_stride(std::extents<int>(), std::array<int, 0>{}, true, true);
+  test_layout_mapping_stride(std::extents<int, 2, 0>(), std::array<int, 2>{1, 2}, true, true);
   test_layout_mapping_stride(std::extents<signed char, 4, 5>(), std::array<signed char, 2>{1, 4}, true);
   test_layout_mapping_stride(std::extents<signed char, 4, 5>(), std::array<signed char, 2>{1, 5}, false);
   test_layout_mapping_stride(std::extents<unsigned, D, 4>(7), std::array<unsigned, 2>{20, 2}, false);

``````````

</details>


https://github.com/llvm/llvm-project/pull/191629


More information about the libcxx-commits mailing list