[libc-commits] [clang-tools-extra] [libc] [flang] [clang] [openmp] [llvm] [lldb] [mlir] [libcxx] [compiler-rt] [libc++][span] P2821R5: span.at() (PR #74994)

Hristo Hristov via libc-commits libc-commits at lists.llvm.org
Sun Dec 17 08:23:19 PST 2023


https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/74994

>From 6e26ca239c49e1b7d9ab72217db7339e92df163f Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Sun, 10 Dec 2023 14:16:02 +0200
Subject: [PATCH 01/15] [libc++][span] P2821R5: span.at()

---
 libcxx/include/span                           |  30 +++
 .../views/views.span/span.elem/at.pass.cpp    | 246 ++++++++++++++++++
 .../views.span/span.elem/op_idx.pass.cpp      |   1 -
 3 files changed, 276 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp

diff --git a/libcxx/include/span b/libcxx/include/span
index 69b0a2875e26cc..b015d7cf1c15b6 100644
--- a/libcxx/include/span
+++ b/libcxx/include/span
@@ -92,6 +92,7 @@ public:
 
     // [span.elem], span element access
     constexpr reference operator[](size_type idx) const;
+    constexpr reference at(size_type idx) const; // since C++26
     constexpr reference front() const;
     constexpr reference back() const;
     constexpr pointer data() const noexcept;
@@ -146,6 +147,9 @@ template<class R>
 #include <__utility/forward.h>
 #include <array>        // for array
 #include <cstddef>      // for byte
+#if _LIBCPP_STD_VER >= 26
+#  include <stdexcept>
+#endif
 #include <version>
 
 // standard-mandated includes
@@ -343,6 +347,15 @@ public:
         return __data_[__idx];
     }
 
+#  if _LIBCPP_STD_VER >= 26
+    _LIBCPP_HIDE_FROM_ABI constexpr reference at(size_type __idx) const {
+      if (__idx >= size()) {
+        __throw_out_of_range();
+      }
+      return *(data() + __idx);
+    }
+#  endif
+
     _LIBCPP_INLINE_VISIBILITY constexpr reference front() const noexcept
     {
         _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), "span<T, N>::front() on empty span");
@@ -383,6 +396,10 @@ public:
 
 private:
     pointer    __data_;
+
+#  if _LIBCPP_STD_VER >= 26
+    _LIBCPP_NORETURN _LIBCPP_HIDE_FROM_ABI void __throw_out_of_range() const { std::__throw_out_of_range("span"); }
+#  endif
 };
 
 
@@ -510,6 +527,15 @@ public:
         return __data_[__idx];
     }
 
+#  if _LIBCPP_STD_VER >= 26
+    _LIBCPP_HIDE_FROM_ABI constexpr reference at(size_type __idx) const {
+      if (__idx >= size()) {
+        __throw_out_of_range();
+      }
+      return *(data() + __idx);
+    }
+#  endif
+
     _LIBCPP_INLINE_VISIBILITY constexpr reference front() const noexcept
     {
         _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), "span<T>::front() on empty span");
@@ -552,6 +578,10 @@ public:
 private:
     pointer   __data_;
     size_type __size_;
+
+#  if _LIBCPP_STD_VER >= 26
+    _LIBCPP_NORETURN _LIBCPP_HIDE_FROM_ABI void __throw_out_of_range() const { std::__throw_out_of_range("span"); }
+#  endif
 };
 
 template <class _Tp, size_t _Extent>
diff --git a/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp b/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp
new file mode 100644
index 00000000000000..2a9ce2baeec1a5
--- /dev/null
+++ b/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp
@@ -0,0 +1,246 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <span>
+
+// constexpr reference at(size_type idx) const; // since C++26
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <span>
+#include <stdexcept>
+#include <vector>
+
+#include "test_macros.h"
+
+// template <typename Span>
+// constexpr bool testConstexprSpan(Span sp, std::size_t idx)
+// {
+//     LIBCPP_ASSERT(noexcept(sp[idx]));
+
+//     typename Span::reference r1 = sp[idx];
+//     typename Span::reference r2 = *(sp.data() + idx);
+
+//     return r1 == r2;
+// }
+
+// template <typename Span>
+// void testRuntimeSpan(Span sp, std::size_t idx)
+// {
+//     LIBCPP_ASSERT(noexcept(sp[idx]));
+
+//     typename Span::reference r1 = sp[idx];
+//     typename Span::reference r2 = *(sp.data() + idx);
+
+//     assert(r1 == r2);
+// }
+
+// struct A{};
+// constexpr int iArr1[] = { 0,  1,  2,  3,  4,  5,  6,  7,  8,  9};
+//           int iArr2[] = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
+
+// int main(int, char**)
+// {
+//     static_assert(testConstexprSpan(std::span<const int>(iArr1, 1), 0), "");
+
+//     static_assert(testConstexprSpan(std::span<const int>(iArr1, 2), 0), "");
+//     static_assert(testConstexprSpan(std::span<const int>(iArr1, 2), 1), "");
+
+//     static_assert(testConstexprSpan(std::span<const int>(iArr1, 3), 0), "");
+//     static_assert(testConstexprSpan(std::span<const int>(iArr1, 3), 1), "");
+//     static_assert(testConstexprSpan(std::span<const int>(iArr1, 3), 2), "");
+
+//     static_assert(testConstexprSpan(std::span<const int>(iArr1, 4), 0), "");
+//     static_assert(testConstexprSpan(std::span<const int>(iArr1, 4), 1), "");
+//     static_assert(testConstexprSpan(std::span<const int>(iArr1, 4), 2), "");
+//     static_assert(testConstexprSpan(std::span<const int>(iArr1, 4), 3), "");
+
+//     static_assert(testConstexprSpan(std::span<const int, 1>(iArr1, 1), 0), "");
+
+//     static_assert(testConstexprSpan(std::span<const int, 2>(iArr1, 2), 0), "");
+//     static_assert(testConstexprSpan(std::span<const int, 2>(iArr1, 2), 1), "");
+
+//     static_assert(testConstexprSpan(std::span<const int, 3>(iArr1, 3), 0), "");
+//     static_assert(testConstexprSpan(std::span<const int, 3>(iArr1, 3), 1), "");
+//     static_assert(testConstexprSpan(std::span<const int, 3>(iArr1, 3), 2), "");
+
+//     static_assert(testConstexprSpan(std::span<const int, 4>(iArr1, 4), 0), "");
+//     static_assert(testConstexprSpan(std::span<const int, 4>(iArr1, 4), 1), "");
+//     static_assert(testConstexprSpan(std::span<const int, 4>(iArr1, 4), 2), "");
+//     static_assert(testConstexprSpan(std::span<const int, 4>(iArr1, 4), 3), "");
+
+//     testRuntimeSpan(std::span<int>(iArr2, 1), 0);
+
+//     testRuntimeSpan(std::span<int>(iArr2, 2), 0);
+//     testRuntimeSpan(std::span<int>(iArr2, 2), 1);
+
+//     testRuntimeSpan(std::span<int>(iArr2, 3), 0);
+//     testRuntimeSpan(std::span<int>(iArr2, 3), 1);
+//     testRuntimeSpan(std::span<int>(iArr2, 3), 2);
+
+//     testRuntimeSpan(std::span<int>(iArr2, 4), 0);
+//     testRuntimeSpan(std::span<int>(iArr2, 4), 1);
+//     testRuntimeSpan(std::span<int>(iArr2, 4), 2);
+//     testRuntimeSpan(std::span<int>(iArr2, 4), 3);
+
+//     testRuntimeSpan(std::span<int, 1>(iArr2, 1), 0);
+
+//     testRuntimeSpan(std::span<int, 2>(iArr2, 2), 0);
+//     testRuntimeSpan(std::span<int, 2>(iArr2, 2), 1);
+
+//     testRuntimeSpan(std::span<int, 3>(iArr2, 3), 0);
+//     testRuntimeSpan(std::span<int, 3>(iArr2, 3), 1);
+//     testRuntimeSpan(std::span<int, 3>(iArr2, 3), 2);
+
+//     testRuntimeSpan(std::span<int, 4>(iArr2, 4), 0);
+//     testRuntimeSpan(std::span<int, 4>(iArr2, 4), 1);
+//     testRuntimeSpan(std::span<int, 4>(iArr2, 4), 2);
+//     testRuntimeSpan(std::span<int, 4>(iArr2, 4), 3);
+
+//     std::string s;
+//     testRuntimeSpan(std::span<std::string>   (&s, 1), 0);
+//     testRuntimeSpan(std::span<std::string, 1>(&s, 1), 0);
+
+//   return 0;
+// }
+
+constexpr bool test() {
+  //   {
+  //     typedef double T;
+  //     typedef std::array<T, 3> C;
+  //     C const c                      = {1, 2, 3.5};
+  //     typename C::const_reference r1 = c.at(0);
+  //     assert(r1 == 1);
+
+  //     typename C::const_reference r2 = c.at(2);
+  //     assert(r2 == 3.5);
+  //   }
+
+  const auto testSpan =
+      [](auto span, int idx, int expectedValue) {
+        {
+          std::same_as<decltype(span)::reference> elem = span.at(idx);
+          assert(elem == expectedValue);
+        }
+
+        {
+          std::same_as<decltype(span)::const_reference> elem = std::as_const(span).at(idx);
+          assert(elem == expectedValue);
+        }
+      }
+
+  // With static extent
+
+  std::array arr{0, 1, 2, 3, 4, 5, 9084};
+  std::span arrSpan{ar};
+
+  assert(std::dynamic_extent != arrSpan.extent);
+
+  testSpan(arrSpan, 0, 0);
+  testSpan(arrSpan, 1, 1);
+  testSpan(arrSpan, 5, 9084);
+
+  {
+    std::same_as<decltype(arrSpan)::reference> arrElem = arrSpan.at(1);
+    assert(arrElem == 1);
+  }
+
+  {
+    std::same_as<decltype(arrSpan)::const_reference> arrElem = std::as_const(arrSpan).at(1);
+    assert(arrElem == 1);
+  }
+
+  // With dynamic extent
+
+  std::vector vec{0, 1, 2, 3, 4, 5};
+  std::span vecSpan{vec};
+  
+  assert(std::dynamic_extent == vecSpan.extent)
+
+  {
+    std::same_as<decltype(vecSpan)::reference> vecElem = vecSpan.at(1);
+    assert(vec_elem == 1);
+  }
+
+  {
+    std::same_as<decltype(vecSpan)::reference> vecElem = std::as_const(vecSpan).at(1);
+    assert(vec_elem == 1);
+  }
+
+  return true;
+}
+
+void test_exceptions() {
+#ifndef TEST_HAS_NO_EXCEPTIONS
+  // With static extent
+  {
+    const std::array arr{1, 2, 3, 4};
+
+    try {
+      TEST_IGNORE_NODISCARD arr.at(4);
+      assert(false);
+    } catch (std::out_of_range const&) {
+      // pass
+    } catch (...) {
+      assert(false);
+    }
+  }
+
+  {
+    const std::array<int, 0> arr{};
+
+    try {
+      TEST_IGNORE_NODISCARD arr.at(0);
+      assert(false);
+    } catch (std::out_of_range const&) {
+      // pass
+    } catch (...) {
+      assert(false);
+    }
+  }
+
+  // With dynamic extent
+
+  {
+    const std::vector vec{1, 2, 3, 4};
+
+    try {
+      TEST_IGNORE_NODISCARD vec.at(4);
+      assert(false);
+    } catch (std::out_of_range const&) {
+      // pass
+    } catch (...) {
+      assert(false);
+    }
+  }
+
+  {
+    const std::vector<int> vec{};
+
+    try {
+      TEST_IGNORE_NODISCARD vec.at(0);
+      assert(false);
+    } catch (std::out_of_range const&) {
+      // pass
+    } catch (...) {
+      assert(false);
+    }
+  }
+#endif
+}
+
+int main(int, char**) {
+  test();
+  test_exceptions();
+  static_assert(test());
+
+  return 0;
+}
\ No newline at end of file
diff --git a/libcxx/test/std/containers/views/views.span/span.elem/op_idx.pass.cpp b/libcxx/test/std/containers/views/views.span/span.elem/op_idx.pass.cpp
index e46fd267ef5cc5..b7f36c57585881 100644
--- a/libcxx/test/std/containers/views/views.span/span.elem/op_idx.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.elem/op_idx.pass.cpp
@@ -41,7 +41,6 @@ void testRuntimeSpan(Span sp, std::size_t idx)
     assert(r1 == r2);
 }
 
-struct A{};
 constexpr int iArr1[] = { 0,  1,  2,  3,  4,  5,  6,  7,  8,  9};
           int iArr2[] = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
 

>From ed7ee83fa9b10a37295d050e1edc538b05a22504 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Sun, 10 Dec 2023 16:03:08 +0200
Subject: [PATCH 02/15] Cleanup tests

---
 libcxx/docs/ReleaseNotes/18.rst               |   1 +
 libcxx/docs/Status/Cxx2cPapers.csv            |   2 +-
 .../views/views.span/span.elem/at.pass.cpp    | 139 ++----------------
 z_libcxx-dev-tools                            |   1 +
 4 files changed, 18 insertions(+), 125 deletions(-)
 create mode 160000 z_libcxx-dev-tools

diff --git a/libcxx/docs/ReleaseNotes/18.rst b/libcxx/docs/ReleaseNotes/18.rst
index abefe4c28ca958..05542f034e63b4 100644
--- a/libcxx/docs/ReleaseNotes/18.rst
+++ b/libcxx/docs/ReleaseNotes/18.rst
@@ -54,6 +54,7 @@ Implemented Papers
 - P2871R3 - Remove Deprecated Unicode Conversion Facets from C++26
 - P2870R3 - Remove basic_string::reserve()
 - P2909R4 - Fix formatting of code units as integers (Dude, where’s my ``char``?)
+- P2821R5 - span.at()
 
 
 Improvements and New Features
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index 1d071b7ebcb4a7..b97dc44795ac82 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -35,7 +35,7 @@
 "`P2909R4 <https://wg21.link/P2909R4>`__","LWG","Fix formatting of code units as integers (Dude, where’s my ``char``?)","Kona November 2023","|Complete|","18.0","|format| |DR|"
 "`P0952R2 <https://wg21.link/P0952R2>`__","LWG","A new specification for ``std::generate_canonical``","Kona November 2023","","",""
 "`P2447R6 <https://wg21.link/P2447R6>`__","LWG","``std::span`` over an initializer list","Kona November 2023","","",""
-"`P2821R5 <https://wg21.link/P2821R5>`__","LWG","``span.at()``","Kona November 2023","","",""
+"`P2821R5 <https://wg21.link/P2821R5>`__","LWG","``span.at()``","Kona November 2023","|Complete|","18.0",""
 "`P2868R3 <https://wg21.link/P2868R3>`__","LWG","Remove Deprecated ``std::allocator`` Typedef From C++26","Kona November 2023","","",""
 "`P2870R3 <https://wg21.link/P2870R3>`__","LWG","Remove ``basic_string::reserve()`` From C++26","Kona November 2023","|Complete|","18.0",""
 "`P2871R3 <https://wg21.link/P2871R3>`__","LWG","Remove Deprecated Unicode Conversion Facets from C++26","Kona November 2023","|Complete|","18.0",""
diff --git a/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp b/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp
index 2a9ce2baeec1a5..8daaa6bb6fa127 100644
--- a/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp
@@ -17,144 +17,35 @@
 #include <concepts>
 #include <span>
 #include <stdexcept>
+#include <utility>
 #include <vector>
 
 #include "test_macros.h"
 
-// template <typename Span>
-// constexpr bool testConstexprSpan(Span sp, std::size_t idx)
-// {
-//     LIBCPP_ASSERT(noexcept(sp[idx]));
-
-//     typename Span::reference r1 = sp[idx];
-//     typename Span::reference r2 = *(sp.data() + idx);
-
-//     return r1 == r2;
-// }
-
-// template <typename Span>
-// void testRuntimeSpan(Span sp, std::size_t idx)
-// {
-//     LIBCPP_ASSERT(noexcept(sp[idx]));
-
-//     typename Span::reference r1 = sp[idx];
-//     typename Span::reference r2 = *(sp.data() + idx);
-
-//     assert(r1 == r2);
-// }
-
-// struct A{};
-// constexpr int iArr1[] = { 0,  1,  2,  3,  4,  5,  6,  7,  8,  9};
-//           int iArr2[] = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
-
-// int main(int, char**)
-// {
-//     static_assert(testConstexprSpan(std::span<const int>(iArr1, 1), 0), "");
-
-//     static_assert(testConstexprSpan(std::span<const int>(iArr1, 2), 0), "");
-//     static_assert(testConstexprSpan(std::span<const int>(iArr1, 2), 1), "");
-
-//     static_assert(testConstexprSpan(std::span<const int>(iArr1, 3), 0), "");
-//     static_assert(testConstexprSpan(std::span<const int>(iArr1, 3), 1), "");
-//     static_assert(testConstexprSpan(std::span<const int>(iArr1, 3), 2), "");
-
-//     static_assert(testConstexprSpan(std::span<const int>(iArr1, 4), 0), "");
-//     static_assert(testConstexprSpan(std::span<const int>(iArr1, 4), 1), "");
-//     static_assert(testConstexprSpan(std::span<const int>(iArr1, 4), 2), "");
-//     static_assert(testConstexprSpan(std::span<const int>(iArr1, 4), 3), "");
-
-//     static_assert(testConstexprSpan(std::span<const int, 1>(iArr1, 1), 0), "");
-
-//     static_assert(testConstexprSpan(std::span<const int, 2>(iArr1, 2), 0), "");
-//     static_assert(testConstexprSpan(std::span<const int, 2>(iArr1, 2), 1), "");
-
-//     static_assert(testConstexprSpan(std::span<const int, 3>(iArr1, 3), 0), "");
-//     static_assert(testConstexprSpan(std::span<const int, 3>(iArr1, 3), 1), "");
-//     static_assert(testConstexprSpan(std::span<const int, 3>(iArr1, 3), 2), "");
-
-//     static_assert(testConstexprSpan(std::span<const int, 4>(iArr1, 4), 0), "");
-//     static_assert(testConstexprSpan(std::span<const int, 4>(iArr1, 4), 1), "");
-//     static_assert(testConstexprSpan(std::span<const int, 4>(iArr1, 4), 2), "");
-//     static_assert(testConstexprSpan(std::span<const int, 4>(iArr1, 4), 3), "");
-
-//     testRuntimeSpan(std::span<int>(iArr2, 1), 0);
-
-//     testRuntimeSpan(std::span<int>(iArr2, 2), 0);
-//     testRuntimeSpan(std::span<int>(iArr2, 2), 1);
-
-//     testRuntimeSpan(std::span<int>(iArr2, 3), 0);
-//     testRuntimeSpan(std::span<int>(iArr2, 3), 1);
-//     testRuntimeSpan(std::span<int>(iArr2, 3), 2);
-
-//     testRuntimeSpan(std::span<int>(iArr2, 4), 0);
-//     testRuntimeSpan(std::span<int>(iArr2, 4), 1);
-//     testRuntimeSpan(std::span<int>(iArr2, 4), 2);
-//     testRuntimeSpan(std::span<int>(iArr2, 4), 3);
-
-//     testRuntimeSpan(std::span<int, 1>(iArr2, 1), 0);
-
-//     testRuntimeSpan(std::span<int, 2>(iArr2, 2), 0);
-//     testRuntimeSpan(std::span<int, 2>(iArr2, 2), 1);
-
-//     testRuntimeSpan(std::span<int, 3>(iArr2, 3), 0);
-//     testRuntimeSpan(std::span<int, 3>(iArr2, 3), 1);
-//     testRuntimeSpan(std::span<int, 3>(iArr2, 3), 2);
-
-//     testRuntimeSpan(std::span<int, 4>(iArr2, 4), 0);
-//     testRuntimeSpan(std::span<int, 4>(iArr2, 4), 1);
-//     testRuntimeSpan(std::span<int, 4>(iArr2, 4), 2);
-//     testRuntimeSpan(std::span<int, 4>(iArr2, 4), 3);
-
-//     std::string s;
-//     testRuntimeSpan(std::span<std::string>   (&s, 1), 0);
-//     testRuntimeSpan(std::span<std::string, 1>(&s, 1), 0);
-
-//   return 0;
-// }
+constexpr void testSpan(auto span, int idx, int expectedValue) {
+  std::same_as<typename decltype(span)::reference> decltype(auto) elem = span.at(idx);
+  assert(elem == expectedValue);
+}
 
 constexpr bool test() {
-  //   {
-  //     typedef double T;
-  //     typedef std::array<T, 3> C;
-  //     C const c                      = {1, 2, 3.5};
-  //     typename C::const_reference r1 = c.at(0);
-  //     assert(r1 == 1);
-
-  //     typename C::const_reference r2 = c.at(2);
-  //     assert(r2 == 3.5);
-  //   }
-
-  const auto testSpan =
-      [](auto span, int idx, int expectedValue) {
-        {
-          std::same_as<decltype(span)::reference> elem = span.at(idx);
-          assert(elem == expectedValue);
-        }
-
-        {
-          std::same_as<decltype(span)::const_reference> elem = std::as_const(span).at(idx);
-          assert(elem == expectedValue);
-        }
-      }
-
   // With static extent
 
   std::array arr{0, 1, 2, 3, 4, 5, 9084};
-  std::span arrSpan{ar};
+  std::span arrSpan{arr};
 
   assert(std::dynamic_extent != arrSpan.extent);
 
   testSpan(arrSpan, 0, 0);
   testSpan(arrSpan, 1, 1);
-  testSpan(arrSpan, 5, 9084);
+  testSpan(arrSpan, 6, 9084);
 
   {
-    std::same_as<decltype(arrSpan)::reference> arrElem = arrSpan.at(1);
+    std::same_as<typename decltype(arrSpan)::reference> decltype(auto) arrElem = arrSpan.at(1);
     assert(arrElem == 1);
   }
 
   {
-    std::same_as<decltype(arrSpan)::const_reference> arrElem = std::as_const(arrSpan).at(1);
+    std::same_as<typename decltype(arrSpan)::reference> decltype(auto) arrElem = std::as_const(arrSpan).at(1);
     assert(arrElem == 1);
   }
 
@@ -162,17 +53,17 @@ constexpr bool test() {
 
   std::vector vec{0, 1, 2, 3, 4, 5};
   std::span vecSpan{vec};
-  
-  assert(std::dynamic_extent == vecSpan.extent)
+
+  assert(std::dynamic_extent == vecSpan.extent);
 
   {
-    std::same_as<decltype(vecSpan)::reference> vecElem = vecSpan.at(1);
-    assert(vec_elem == 1);
+    std::same_as<typename decltype(vecSpan)::reference> decltype(auto) vecElem = vecSpan.at(1);
+    assert(vecElem == 1);
   }
 
   {
-    std::same_as<decltype(vecSpan)::reference> vecElem = std::as_const(vecSpan).at(1);
-    assert(vec_elem == 1);
+    std::same_as<typename decltype(vecSpan)::reference> decltype(auto) vecElem = std::as_const(vecSpan).at(1);
+    assert(vecElem == 1);
   }
 
   return true;
diff --git a/z_libcxx-dev-tools b/z_libcxx-dev-tools
new file mode 160000
index 00000000000000..229818e01844e2
--- /dev/null
+++ b/z_libcxx-dev-tools
@@ -0,0 +1 @@
+Subproject commit 229818e01844e2cad56818bf06703600071848c0

>From f02f8a0e8667fec1e5b5e16a13a9b5e52c52e58d Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Sun, 10 Dec 2023 18:25:42 +0200
Subject: [PATCH 03/15] Removed bad file

---
 z_libcxx-dev-tools | 1 -
 1 file changed, 1 deletion(-)
 delete mode 160000 z_libcxx-dev-tools

diff --git a/z_libcxx-dev-tools b/z_libcxx-dev-tools
deleted file mode 160000
index 229818e01844e2..00000000000000
--- a/z_libcxx-dev-tools
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 229818e01844e2cad56818bf06703600071848c0

>From e8e8457304f87e4780144fe3bc3bf918c335b726 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Sun, 10 Dec 2023 18:31:46 +0200
Subject: [PATCH 04/15] Fixed test_exceptions

---
 .../views/views.span/span.elem/at.pass.cpp     | 18 +++++++++++-------
 1 file changed, 11 insertions(+), 7 deletions(-)

diff --git a/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp b/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp
index 8daaa6bb6fa127..68c2fca62a67cf 100644
--- a/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp
@@ -73,10 +73,11 @@ void test_exceptions() {
 #ifndef TEST_HAS_NO_EXCEPTIONS
   // With static extent
   {
-    const std::array arr{1, 2, 3, 4};
+    std::array arr{1, 2, 3, 4};
+    const std::span arrSpan{arr};
 
     try {
-      TEST_IGNORE_NODISCARD arr.at(4);
+      TEST_IGNORE_NODISCARD arrSpan.at(4);
       assert(false);
     } catch (std::out_of_range const&) {
       // pass
@@ -86,10 +87,11 @@ void test_exceptions() {
   }
 
   {
-    const std::array<int, 0> arr{};
+    std::array<int, 0> arr{};
+    const std::span arrSpan{arr};
 
     try {
-      TEST_IGNORE_NODISCARD arr.at(0);
+      TEST_IGNORE_NODISCARD arrSpan.at(0);
       assert(false);
     } catch (std::out_of_range const&) {
       // pass
@@ -101,7 +103,8 @@ void test_exceptions() {
   // With dynamic extent
 
   {
-    const std::vector vec{1, 2, 3, 4};
+    std::vector vec{1, 2, 3, 4};
+    const std::span vecSpan{vec};
 
     try {
       TEST_IGNORE_NODISCARD vec.at(4);
@@ -114,7 +117,8 @@ void test_exceptions() {
   }
 
   {
-    const std::vector<int> vec{};
+    std::vector<int> vec{};
+    const std::span vecSpan{vec};
 
     try {
       TEST_IGNORE_NODISCARD vec.at(0);
@@ -134,4 +138,4 @@ int main(int, char**) {
   static_assert(test());
 
   return 0;
-}
\ No newline at end of file
+}

>From 9340bc0c3be951e6431926ebb8c7f3a5f71a7d1c Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Sun, 10 Dec 2023 19:12:42 +0200
Subject: [PATCH 05/15] Fixed tests

---
 .../views/views.span/span.elem/at.pass.cpp    | 43 ++++++++-----------
 1 file changed, 18 insertions(+), 25 deletions(-)

diff --git a/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp b/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp
index 68c2fca62a67cf..f3d6fbd9fb28aa 100644
--- a/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp
@@ -23,8 +23,17 @@
 #include "test_macros.h"
 
 constexpr void testSpan(auto span, int idx, int expectedValue) {
-  std::same_as<typename decltype(span)::reference> decltype(auto) elem = span.at(idx);
-  assert(elem == expectedValue);
+  // non-const
+  {
+    std::same_as<typename decltype(span)::reference> decltype(auto) elem = span.at(idx);
+    assert(elem == expectedValue);
+  }
+
+  // const
+  {
+    std::same_as<typename decltype(span)::reference> decltype(auto) elem = std::as_const(span).at(idx);
+    assert(elem == expectedValue);
+  }
 }
 
 constexpr bool test() {
@@ -39,32 +48,16 @@ constexpr bool test() {
   testSpan(arrSpan, 1, 1);
   testSpan(arrSpan, 6, 9084);
 
-  {
-    std::same_as<typename decltype(arrSpan)::reference> decltype(auto) arrElem = arrSpan.at(1);
-    assert(arrElem == 1);
-  }
-
-  {
-    std::same_as<typename decltype(arrSpan)::reference> decltype(auto) arrElem = std::as_const(arrSpan).at(1);
-    assert(arrElem == 1);
-  }
-
   // With dynamic extent
 
-  std::vector vec{0, 1, 2, 3, 4, 5};
+  std::vector vec{0, 1, 2, 3, 4, 5, 9084};
   std::span vecSpan{vec};
 
   assert(std::dynamic_extent == vecSpan.extent);
 
-  {
-    std::same_as<typename decltype(vecSpan)::reference> decltype(auto) vecElem = vecSpan.at(1);
-    assert(vecElem == 1);
-  }
-
-  {
-    std::same_as<typename decltype(vecSpan)::reference> decltype(auto) vecElem = std::as_const(vecSpan).at(1);
-    assert(vecElem == 1);
-  }
+  testSpan(vecSpan, 0, 0);
+  testSpan(vecSpan, 1, 1);
+  testSpan(vecSpan, 6, 9084);
 
   return true;
 }
@@ -77,7 +70,7 @@ void test_exceptions() {
     const std::span arrSpan{arr};
 
     try {
-      TEST_IGNORE_NODISCARD arrSpan.at(4);
+      TEST_IGNORE_NODISCARD arrSpan.at(arr.size() + 1);
       assert(false);
     } catch (std::out_of_range const&) {
       // pass
@@ -107,7 +100,7 @@ void test_exceptions() {
     const std::span vecSpan{vec};
 
     try {
-      TEST_IGNORE_NODISCARD vec.at(4);
+      TEST_IGNORE_NODISCARD vecSpan.at(vec.size() + 1);
       assert(false);
     } catch (std::out_of_range const&) {
       // pass
@@ -121,7 +114,7 @@ void test_exceptions() {
     const std::span vecSpan{vec};
 
     try {
-      TEST_IGNORE_NODISCARD vec.at(0);
+      TEST_IGNORE_NODISCARD vecSpan.at(0);
       assert(false);
     } catch (std::out_of_range const&) {
       // pass

>From 431b502a10e9e60462a05de37db3e55cc0ff1e7e Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Sun, 10 Dec 2023 19:19:00 +0200
Subject: [PATCH 06/15] Minor tweaks

---
 .../views/views.span/span.elem/at.pass.cpp    | 34 ++++++++++---------
 1 file changed, 18 insertions(+), 16 deletions(-)

diff --git a/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp b/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp
index f3d6fbd9fb28aa..75a45171185cf3 100644
--- a/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp
@@ -22,7 +22,7 @@
 
 #include "test_macros.h"
 
-constexpr void testSpan(auto span, int idx, int expectedValue) {
+constexpr void testSpanAt(auto span, int idx, int expectedValue) {
   // non-const
   {
     std::same_as<typename decltype(span)::reference> decltype(auto) elem = span.at(idx);
@@ -38,26 +38,28 @@ constexpr void testSpan(auto span, int idx, int expectedValue) {
 
 constexpr bool test() {
   // With static extent
+  {
+    std::array arr{0, 1, 2, 3, 4, 5, 9084};
+    std::span arrSpan{arr};
 
-  std::array arr{0, 1, 2, 3, 4, 5, 9084};
-  std::span arrSpan{arr};
-
-  assert(std::dynamic_extent != arrSpan.extent);
+    assert(std::dynamic_extent != arrSpan.extent);
 
-  testSpan(arrSpan, 0, 0);
-  testSpan(arrSpan, 1, 1);
-  testSpan(arrSpan, 6, 9084);
+    testSpanAt(arrSpan, 0, 0);
+    testSpanAt(arrSpan, 1, 1);
+    testSpanAt(arrSpan, 6, 9084);
+  }
 
   // With dynamic extent
+  {
+    std::vector vec{0, 1, 2, 3, 4, 5, 9084};
+    std::span vecSpan{vec};
 
-  std::vector vec{0, 1, 2, 3, 4, 5, 9084};
-  std::span vecSpan{vec};
-
-  assert(std::dynamic_extent == vecSpan.extent);
+    assert(std::dynamic_extent == vecSpan.extent);
 
-  testSpan(vecSpan, 0, 0);
-  testSpan(vecSpan, 1, 1);
-  testSpan(vecSpan, 6, 9084);
+    testSpanAt(vecSpan, 0, 0);
+    testSpanAt(vecSpan, 1, 1);
+    testSpanAt(vecSpan, 6, 9084);
+  }
 
   return true;
 }
@@ -122,7 +124,7 @@ void test_exceptions() {
       assert(false);
     }
   }
-#endif
+#endif // TEST_HAS_NO_EXCEPTIONS
 }
 
 int main(int, char**) {

>From 2f52b03bcec7ca93f042be69550659e71098b029 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Sun, 10 Dec 2023 22:44:28 +0200
Subject: [PATCH 07/15] Adressed review comments

---
 libcxx/docs/FeatureTestMacroTable.rst         |  2 +
 libcxx/include/span                           | 28 +++-----
 libcxx/include/version                        |  2 +
 .../views/views.span/span.elem/at.pass.cpp    | 70 +++++++++++--------
 .../span.version.compile.pass.cpp             | 28 ++++++++
 .../version.version.compile.pass.cpp          | 28 ++++++++
 .../generate_feature_test_macro_components.py |  5 ++
 z_libcxx-dev-tools                            |  1 +
 8 files changed, 117 insertions(+), 47 deletions(-)
 create mode 160000 z_libcxx-dev-tools

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index d09f65b7cadc0e..75028841fbb7f2 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -436,6 +436,8 @@ Status
     --------------------------------------------------- -----------------
     ``__cpp_lib_smart_ptr_owner_equality``              *unimplemented*
     --------------------------------------------------- -----------------
+    ``__cpp_lib_span_at``                               ``202311L``
+    --------------------------------------------------- -----------------
     ``__cpp_lib_span_initializer_list``                 *unimplemented*
     --------------------------------------------------- -----------------
     ``__cpp_lib_sstream_from_string_view``              *unimplemented*
diff --git a/libcxx/include/span b/libcxx/include/span
index 9af8dabb3ebd34..8c52b00e4ae967 100644
--- a/libcxx/include/span
+++ b/libcxx/include/span
@@ -147,9 +147,7 @@ template<class R>
 #include <__utility/forward.h>
 #include <array>        // for array
 #include <cstddef>      // for byte
-#if _LIBCPP_STD_VER >= 26
-#  include <stdexcept>
-#endif
+#include <stdexcept>
 #include <version>
 
 // standard-mandated includes
@@ -348,11 +346,11 @@ public:
     }
 
 #  if _LIBCPP_STD_VER >= 26
-    _LIBCPP_HIDE_FROM_ABI constexpr reference at(size_type __idx) const {
-      if (__idx >= size()) {
-        __throw_out_of_range();
+    _LIBCPP_HIDE_FROM_ABI constexpr reference at(size_type __index) const {
+      if (__index >= size()) {
+        std::__throw_out_of_range("span");
       }
-      return *(data() + __idx);
+      return __data_[__index];
     }
 #  endif
 
@@ -396,10 +394,6 @@ public:
 
 private:
     pointer    __data_;
-
-#  if _LIBCPP_STD_VER >= 26
-    _LIBCPP_NORETURN _LIBCPP_HIDE_FROM_ABI void __throw_out_of_range() const { std::__throw_out_of_range("span"); }
-#  endif
 };
 
 
@@ -528,11 +522,11 @@ public:
     }
 
 #  if _LIBCPP_STD_VER >= 26
-    _LIBCPP_HIDE_FROM_ABI constexpr reference at(size_type __idx) const {
-      if (__idx >= size()) {
-        __throw_out_of_range();
+    _LIBCPP_HIDE_FROM_ABI constexpr reference at(size_type __index) const {
+      if (__index >= size()) {
+        std::__throw_out_of_range("span");
       }
-      return *(data() + __idx);
+      return __data_[__index];
     }
 #  endif
 
@@ -578,10 +572,6 @@ public:
 private:
     pointer   __data_;
     size_type __size_;
-
-#  if _LIBCPP_STD_VER >= 26
-    _LIBCPP_NORETURN _LIBCPP_HIDE_FROM_ABI void __throw_out_of_range() const { std::__throw_out_of_range("span"); }
-#  endif
 };
 
 template <class _Tp, size_t _Extent>
diff --git a/libcxx/include/version b/libcxx/include/version
index a91c344c436090..0c2cc09e54c763 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -201,6 +201,7 @@ __cpp_lib_smart_ptr_for_overwrite                       202002L <memory>
 __cpp_lib_smart_ptr_owner_equality                      202306L <memory>
 __cpp_lib_source_location                               201907L <source_location>
 __cpp_lib_span                                          202002L <span>
+__cpp_lib_span_at                                       202311L <span>
 __cpp_lib_span_initializer_list                         202311L <span>
 __cpp_lib_spanstream                                    202106L <spanstream>
 __cpp_lib_ssize                                         201902L <iterator>
@@ -505,6 +506,7 @@ __cpp_lib_within_lifetime                               202306L <type_traits>
 // # define __cpp_lib_rcu                                  202306L
 // # define __cpp_lib_saturation_arithmetic                202311L
 // # define __cpp_lib_smart_ptr_owner_equality             202306L
+# define __cpp_lib_span_at                              202311L
 // # define __cpp_lib_span_initializer_list                202311L
 // # define __cpp_lib_sstream_from_string_view             202306L
 // # define __cpp_lib_submdspan                            202306L
diff --git a/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp b/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp
index 75a45171185cf3..fb5519365ae93a 100644
--- a/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp
@@ -15,23 +15,34 @@
 #include <array>
 #include <cassert>
 #include <concepts>
+#include <limits>
 #include <span>
 #include <stdexcept>
+#include <string>
 #include <utility>
 #include <vector>
+#include <iostream>
 
 #include "test_macros.h"
 
-constexpr void testSpanAt(auto span, int idx, int expectedValue) {
+constexpr void testSpanAt(auto& container, bool hasDynamicExtent, int index, int expectedValue) {
+  std::span anySpan{container};
+
+  if (hasDynamicExtent) {
+    assert(std::dynamic_extent == anySpan.extent);
+  } else {
+    assert(std::dynamic_extent != anySpan.extent);
+  }
+
   // non-const
   {
-    std::same_as<typename decltype(span)::reference> decltype(auto) elem = span.at(idx);
+    std::same_as<typename decltype(anySpan)::reference> decltype(auto) elem = anySpan.at(index);
     assert(elem == expectedValue);
   }
 
   // const
   {
-    std::same_as<typename decltype(span)::reference> decltype(auto) elem = std::as_const(span).at(idx);
+    std::same_as<typename decltype(anySpan)::reference> decltype(auto) elem = std::as_const(anySpan).at(index);
     assert(elem == expectedValue);
   }
 }
@@ -39,26 +50,22 @@ constexpr void testSpanAt(auto span, int idx, int expectedValue) {
 constexpr bool test() {
   // With static extent
   {
-    std::array arr{0, 1, 2, 3, 4, 5, 9084};
-    std::span arrSpan{arr};
+    std::array arr{0, 1, 2, 3, 4, 5, 9084, std::numeric_limits<int>::max()};
 
-    assert(std::dynamic_extent != arrSpan.extent);
-
-    testSpanAt(arrSpan, 0, 0);
-    testSpanAt(arrSpan, 1, 1);
-    testSpanAt(arrSpan, 6, 9084);
+    testSpanAt(arr, false, 0, 0);
+    testSpanAt(arr, false, 1, 1);
+    testSpanAt(arr, false, 6, 9084);
+    testSpanAt(arr, false, 7, std::numeric_limits<int>::max());
   }
 
   // With dynamic extent
   {
-    std::vector vec{0, 1, 2, 3, 4, 5, 9084};
-    std::span vecSpan{vec};
-
-    assert(std::dynamic_extent == vecSpan.extent);
+    std::vector vec{0, 1, 2, 3, 4, 5, 9084, std::numeric_limits<int>::max()};
 
-    testSpanAt(vecSpan, 0, 0);
-    testSpanAt(vecSpan, 1, 1);
-    testSpanAt(vecSpan, 6, 9084);
+    testSpanAt(vec, true, 0, 0);
+    testSpanAt(vec, true, 1, 1);
+    testSpanAt(vec, true, 6, 9084);
+    testSpanAt(vec, true, 7, std::numeric_limits<int>::max());
   }
 
   return true;
@@ -66,16 +73,19 @@ constexpr bool test() {
 
 void test_exceptions() {
 #ifndef TEST_HAS_NO_EXCEPTIONS
+  using namespace std::string_literals;
+
   // With static extent
   {
-    std::array arr{1, 2, 3, 4};
+    std::array arr{0, 1, 2, 3, 4, 5, 9084, std::numeric_limits<int>::max()};
     const std::span arrSpan{arr};
 
     try {
-      TEST_IGNORE_NODISCARD arrSpan.at(arr.size() + 1);
+      std::ignore = arrSpan.at(arr.size());
       assert(false);
-    } catch (std::out_of_range const&) {
+    } catch (const std::out_of_range& e) {
       // pass
+      assert(e.what() == "span"s);
     } catch (...) {
       assert(false);
     }
@@ -86,10 +96,11 @@ void test_exceptions() {
     const std::span arrSpan{arr};
 
     try {
-      TEST_IGNORE_NODISCARD arrSpan.at(0);
+      std::ignore = arrSpan.at(0);
       assert(false);
-    } catch (std::out_of_range const&) {
+    } catch (const std::out_of_range& e) {
       // pass
+      assert(e.what() == "span"s);
     } catch (...) {
       assert(false);
     }
@@ -98,14 +109,15 @@ void test_exceptions() {
   // With dynamic extent
 
   {
-    std::vector vec{1, 2, 3, 4};
+    std::vector vec{0, 1, 2, 3, 4, 5, 9084, std::numeric_limits<int>::max()};
     const std::span vecSpan{vec};
 
     try {
-      TEST_IGNORE_NODISCARD vecSpan.at(vec.size() + 1);
+      std::ignore = vecSpan.at(vec.size());
       assert(false);
-    } catch (std::out_of_range const&) {
+    } catch (const std::out_of_range& e) {
       // pass
+      assert(e.what() == "span"s);
     } catch (...) {
       assert(false);
     }
@@ -116,10 +128,11 @@ void test_exceptions() {
     const std::span vecSpan{vec};
 
     try {
-      TEST_IGNORE_NODISCARD vecSpan.at(0);
+      std::ignore = vecSpan.at(0);
       assert(false);
-    } catch (std::out_of_range const&) {
+    } catch (const std::out_of_range& e) {
       // pass
+      assert(e.what() == "span"s);
     } catch (...) {
       assert(false);
     }
@@ -129,8 +142,9 @@ void test_exceptions() {
 
 int main(int, char**) {
   test();
-  test_exceptions();
   static_assert(test());
 
+  test_exceptions();
+
   return 0;
 }
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/span.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/span.version.compile.pass.cpp
index 355eb1338d945e..dbbbaf4ec7c228 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/span.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/span.version.compile.pass.cpp
@@ -17,6 +17,7 @@
 
 /*  Constant                           Value
     __cpp_lib_span                     202002L [C++20]
+    __cpp_lib_span_at                  202311L [C++26]
     __cpp_lib_span_initializer_list    202311L [C++26]
 */
 
@@ -29,6 +30,10 @@
 #   error "__cpp_lib_span should not be defined before c++20"
 # endif
 
+# ifdef __cpp_lib_span_at
+#   error "__cpp_lib_span_at should not be defined before c++26"
+# endif
+
 # ifdef __cpp_lib_span_initializer_list
 #   error "__cpp_lib_span_initializer_list should not be defined before c++26"
 # endif
@@ -39,6 +44,10 @@
 #   error "__cpp_lib_span should not be defined before c++20"
 # endif
 
+# ifdef __cpp_lib_span_at
+#   error "__cpp_lib_span_at should not be defined before c++26"
+# endif
+
 # ifdef __cpp_lib_span_initializer_list
 #   error "__cpp_lib_span_initializer_list should not be defined before c++26"
 # endif
@@ -49,6 +58,10 @@
 #   error "__cpp_lib_span should not be defined before c++20"
 # endif
 
+# ifdef __cpp_lib_span_at
+#   error "__cpp_lib_span_at should not be defined before c++26"
+# endif
+
 # ifdef __cpp_lib_span_initializer_list
 #   error "__cpp_lib_span_initializer_list should not be defined before c++26"
 # endif
@@ -62,6 +75,10 @@
 #   error "__cpp_lib_span should have the value 202002L in c++20"
 # endif
 
+# ifdef __cpp_lib_span_at
+#   error "__cpp_lib_span_at should not be defined before c++26"
+# endif
+
 # ifdef __cpp_lib_span_initializer_list
 #   error "__cpp_lib_span_initializer_list should not be defined before c++26"
 # endif
@@ -75,6 +92,10 @@
 #   error "__cpp_lib_span should have the value 202002L in c++23"
 # endif
 
+# ifdef __cpp_lib_span_at
+#   error "__cpp_lib_span_at should not be defined before c++26"
+# endif
+
 # ifdef __cpp_lib_span_initializer_list
 #   error "__cpp_lib_span_initializer_list should not be defined before c++26"
 # endif
@@ -88,6 +109,13 @@
 #   error "__cpp_lib_span should have the value 202002L in c++26"
 # endif
 
+# ifndef __cpp_lib_span_at
+#   error "__cpp_lib_span_at should be defined in c++26"
+# endif
+# if __cpp_lib_span_at != 202311L
+#   error "__cpp_lib_span_at should have the value 202311L in c++26"
+# endif
+
 # if !defined(_LIBCPP_VERSION)
 #   ifndef __cpp_lib_span_initializer_list
 #     error "__cpp_lib_span_initializer_list should be defined in c++26"
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index c0d3d554dcf056..ec2258cbb2c8fd 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -186,6 +186,7 @@
     __cpp_lib_smart_ptr_owner_equality               202306L [C++26]
     __cpp_lib_source_location                        201907L [C++20]
     __cpp_lib_span                                   202002L [C++20]
+    __cpp_lib_span_at                                202311L [C++26]
     __cpp_lib_span_initializer_list                  202311L [C++26]
     __cpp_lib_spanstream                             202106L [C++23]
     __cpp_lib_ssize                                  201902L [C++20]
@@ -879,6 +880,10 @@
 #   error "__cpp_lib_span should not be defined before c++20"
 # endif
 
+# ifdef __cpp_lib_span_at
+#   error "__cpp_lib_span_at should not be defined before c++26"
+# endif
+
 # ifdef __cpp_lib_span_initializer_list
 #   error "__cpp_lib_span_initializer_list should not be defined before c++26"
 # endif
@@ -1716,6 +1721,10 @@
 #   error "__cpp_lib_span should not be defined before c++20"
 # endif
 
+# ifdef __cpp_lib_span_at
+#   error "__cpp_lib_span_at should not be defined before c++26"
+# endif
+
 # ifdef __cpp_lib_span_initializer_list
 #   error "__cpp_lib_span_initializer_list should not be defined before c++26"
 # endif
@@ -2733,6 +2742,10 @@
 #   error "__cpp_lib_span should not be defined before c++20"
 # endif
 
+# ifdef __cpp_lib_span_at
+#   error "__cpp_lib_span_at should not be defined before c++26"
+# endif
+
 # ifdef __cpp_lib_span_initializer_list
 #   error "__cpp_lib_span_initializer_list should not be defined before c++26"
 # endif
@@ -4029,6 +4042,10 @@
 #   error "__cpp_lib_span should have the value 202002L in c++20"
 # endif
 
+# ifdef __cpp_lib_span_at
+#   error "__cpp_lib_span_at should not be defined before c++26"
+# endif
+
 # ifdef __cpp_lib_span_initializer_list
 #   error "__cpp_lib_span_initializer_list should not be defined before c++26"
 # endif
@@ -5547,6 +5564,10 @@
 #   error "__cpp_lib_span should have the value 202002L in c++23"
 # endif
 
+# ifdef __cpp_lib_span_at
+#   error "__cpp_lib_span_at should not be defined before c++26"
+# endif
+
 # ifdef __cpp_lib_span_initializer_list
 #   error "__cpp_lib_span_initializer_list should not be defined before c++26"
 # endif
@@ -7284,6 +7305,13 @@
 #   error "__cpp_lib_span should have the value 202002L in c++26"
 # endif
 
+# ifndef __cpp_lib_span_at
+#   error "__cpp_lib_span_at should be defined in c++26"
+# endif
+# if __cpp_lib_span_at != 202311L
+#   error "__cpp_lib_span_at should have the value 202311L in c++26"
+# endif
+
 # if !defined(_LIBCPP_VERSION)
 #   ifndef __cpp_lib_span_initializer_list
 #     error "__cpp_lib_span_initializer_list should be defined in c++26"
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 6a30324397883a..19018bd05c1931 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1082,6 +1082,11 @@ def add_version_header(tc):
             },
             "headers": ["span"],
         },
+        {
+            "name": "__cpp_lib_span_at",
+            "values": {"c++26": 202311},  # P2821R3 span.at()
+            "headers": ["span"],
+        },
         {
             "name": "__cpp_lib_span_initializer_list",
             "values": {"c++26": 202311},  # P2447R6 std::span over an initializer list
diff --git a/z_libcxx-dev-tools b/z_libcxx-dev-tools
new file mode 160000
index 00000000000000..229818e01844e2
--- /dev/null
+++ b/z_libcxx-dev-tools
@@ -0,0 +1 @@
+Subproject commit 229818e01844e2cad56818bf06703600071848c0

>From f3223eaf5131cae761f287b10d9c9c4725b3bff5 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Mon, 11 Dec 2023 10:31:05 +0200
Subject: [PATCH 08/15] Refactored test

---
 .../views/views.span/span.elem/at.pass.cpp    | 70 +++++++++++++------
 1 file changed, 49 insertions(+), 21 deletions(-)

diff --git a/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp b/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp
index fb5519365ae93a..ffa9e825c6d157 100644
--- a/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp
@@ -25,24 +25,19 @@
 
 #include "test_macros.h"
 
-constexpr void testSpanAt(auto& container, bool hasDynamicExtent, int index, int expectedValue) {
-  std::span anySpan{container};
-
-  if (hasDynamicExtent) {
-    assert(std::dynamic_extent == anySpan.extent);
-  } else {
-    assert(std::dynamic_extent != anySpan.extent);
-  }
-
+template <typename ReferenceT>
+constexpr void testSpanAt(auto&& anySpan, int index, int expectedValue) {
   // non-const
   {
-    std::same_as<typename decltype(anySpan)::reference> decltype(auto) elem = anySpan.at(index);
+    // std::same_as<typename decltype(anySpan)::reference> decltype(auto) elem = anySpan.at(index);
+    std::same_as<ReferenceT> decltype(auto) elem = anySpan.at(index);
     assert(elem == expectedValue);
   }
 
   // const
   {
-    std::same_as<typename decltype(anySpan)::reference> decltype(auto) elem = std::as_const(anySpan).at(index);
+    // std::same_as<typename decltype(anySpan)::reference> decltype(auto) elem = std::as_const(anySpan).at(index);
+    std::same_as<ReferenceT> decltype(auto) elem = std::as_const(anySpan).at(index);
     assert(elem == expectedValue);
   }
 }
@@ -50,22 +45,36 @@ constexpr void testSpanAt(auto& container, bool hasDynamicExtent, int index, int
 constexpr bool test() {
   // With static extent
   {
-    std::array arr{0, 1, 2, 3, 4, 5, 9084, std::numeric_limits<int>::max()};
+    std::array arr{0, 1, 2, 3, 4, 5, 9084};
+    std::span arrSpan{arr};
+
+    assert(std::dynamic_extent != arrSpan.extent);
+
+    using ReferenceT = typename decltype(arrSpan)::reference;
 
-    testSpanAt(arr, false, 0, 0);
-    testSpanAt(arr, false, 1, 1);
-    testSpanAt(arr, false, 6, 9084);
-    testSpanAt(arr, false, 7, std::numeric_limits<int>::max());
+    // testSpanAt(arrSpan, 0, 0);
+    // testSpanAt(arrSpan, 1, 1);
+    // testSpanAt(arrSpan, 6, 9084);
+    testSpanAt<ReferenceT>(arrSpan, 0, 0);
+    testSpanAt<ReferenceT>(arrSpan, 1, 1);
+    testSpanAt<ReferenceT>(arrSpan, 6, 9084);
   }
 
   // With dynamic extent
   {
-    std::vector vec{0, 1, 2, 3, 4, 5, 9084, std::numeric_limits<int>::max()};
+    std::vector vec{0, 1, 2, 3, 4, 5, 9084};
+    std::span vecSpan{vec};
+
+    assert(std::dynamic_extent == vecSpan.extent);
+
+    using ReferenceT = typename decltype(vecSpan)::reference;
 
-    testSpanAt(vec, true, 0, 0);
-    testSpanAt(vec, true, 1, 1);
-    testSpanAt(vec, true, 6, 9084);
-    testSpanAt(vec, true, 7, std::numeric_limits<int>::max());
+    // testSpanAt(vecSpan, 0, 0);
+    // testSpanAt(vecSpan, 1, 1);
+    // testSpanAt(vecSpan, 6, 9084);
+    testSpanAt<ReferenceT>(vecSpan, 0, 0);
+    testSpanAt<ReferenceT>(vecSpan, 1, 1);
+    testSpanAt<ReferenceT>(vecSpan, 6, 9084);
   }
 
   return true;
@@ -89,6 +98,16 @@ void test_exceptions() {
     } catch (...) {
       assert(false);
     }
+
+    try {
+      std::ignore = arrSpan.at(arr.size() - 1);
+      // pass
+      assert(arrSpan.at(arr.size() - 1) == std::numeric_limits<int>::max());
+    } catch (const std::out_of_range&) {
+      assert(false);
+    } catch (...) {
+      assert(false);
+    }
   }
 
   {
@@ -121,6 +140,15 @@ void test_exceptions() {
     } catch (...) {
       assert(false);
     }
+
+    try {
+      std::ignore = vecSpan.at(vec.size() - 1);
+      assert(vecSpan.at(vec.size() - 1) == std::numeric_limits<int>::max());
+    } catch (const std::out_of_range& e) {
+      assert(false);
+    } catch (...) {
+      assert(false);
+    }
   }
 
   {

>From f03897a231a5fc86797075913b8f57781825791a Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Mon, 11 Dec 2023 10:32:32 +0200
Subject: [PATCH 09/15] Cleaned up

---
 .../std/containers/views/views.span/span.elem/at.pass.cpp | 8 --------
 1 file changed, 8 deletions(-)

diff --git a/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp b/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp
index ffa9e825c6d157..92484f2e4665d3 100644
--- a/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp
@@ -29,14 +29,12 @@ template <typename ReferenceT>
 constexpr void testSpanAt(auto&& anySpan, int index, int expectedValue) {
   // non-const
   {
-    // std::same_as<typename decltype(anySpan)::reference> decltype(auto) elem = anySpan.at(index);
     std::same_as<ReferenceT> decltype(auto) elem = anySpan.at(index);
     assert(elem == expectedValue);
   }
 
   // const
   {
-    // std::same_as<typename decltype(anySpan)::reference> decltype(auto) elem = std::as_const(anySpan).at(index);
     std::same_as<ReferenceT> decltype(auto) elem = std::as_const(anySpan).at(index);
     assert(elem == expectedValue);
   }
@@ -52,9 +50,6 @@ constexpr bool test() {
 
     using ReferenceT = typename decltype(arrSpan)::reference;
 
-    // testSpanAt(arrSpan, 0, 0);
-    // testSpanAt(arrSpan, 1, 1);
-    // testSpanAt(arrSpan, 6, 9084);
     testSpanAt<ReferenceT>(arrSpan, 0, 0);
     testSpanAt<ReferenceT>(arrSpan, 1, 1);
     testSpanAt<ReferenceT>(arrSpan, 6, 9084);
@@ -69,9 +64,6 @@ constexpr bool test() {
 
     using ReferenceT = typename decltype(vecSpan)::reference;
 
-    // testSpanAt(vecSpan, 0, 0);
-    // testSpanAt(vecSpan, 1, 1);
-    // testSpanAt(vecSpan, 6, 9084);
     testSpanAt<ReferenceT>(vecSpan, 0, 0);
     testSpanAt<ReferenceT>(vecSpan, 1, 1);
     testSpanAt<ReferenceT>(vecSpan, 6, 9084);

>From 077500bb21b98a34f80e7984d0bee9aa31d6976d Mon Sep 17 00:00:00 2001
From: Hristo Hristov <zingam at outlook.com>
Date: Mon, 11 Dec 2023 14:46:49 +0200
Subject: [PATCH 10/15] Try to fix CI: transitive includes

---
 libcxx/test/libcxx/transitive_includes/cxx03.csv | 1 +
 libcxx/test/libcxx/transitive_includes/cxx11.csv | 1 +
 libcxx/test/libcxx/transitive_includes/cxx14.csv | 1 +
 libcxx/test/libcxx/transitive_includes/cxx17.csv | 1 +
 libcxx/test/libcxx/transitive_includes/cxx20.csv | 1 +
 libcxx/test/libcxx/transitive_includes/cxx23.csv | 1 +
 libcxx/test/libcxx/transitive_includes/cxx26.csv | 1 +
 7 files changed, 7 insertions(+)

diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index 7066de65a91372..20e10744bc00b9 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -739,6 +739,7 @@ span functional
 span initializer_list
 span iterator
 span limits
+span stdexcept
 span type_traits
 span version
 sstream cstddef
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index c4dc664d6ca817..df730b9c03b2bb 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -745,6 +745,7 @@ span functional
 span initializer_list
 span iterator
 span limits
+span stdexcept
 span type_traits
 span version
 sstream cstddef
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index 20ee43722d894b..f53b4dc06c183e 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -747,6 +747,7 @@ span functional
 span initializer_list
 span iterator
 span limits
+span stdexcept
 span type_traits
 span version
 sstream cstddef
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index 20ee43722d894b..f53b4dc06c183e 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -747,6 +747,7 @@ span functional
 span initializer_list
 span iterator
 span limits
+span stdexcept
 span type_traits
 span version
 sstream cstddef
diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv
index d256370aac4a4a..e7b8b0faa465d5 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -752,6 +752,7 @@ span functional
 span initializer_list
 span iterator
 span limits
+span stdexcept
 span type_traits
 span version
 sstream cstddef
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index 9edc283236480e..78148f7e70a758 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -515,6 +515,7 @@ span array
 span cstddef
 span initializer_list
 span limits
+span stdexcept
 span version
 sstream cstddef
 sstream istream
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index 9edc283236480e..78148f7e70a758 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -515,6 +515,7 @@ span array
 span cstddef
 span initializer_list
 span limits
+span stdexcept
 span version
 sstream cstddef
 sstream istream

>From 86ef4053380ac223f8ff73bbf8454c56e9ab0cf7 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <zingam at outlook.com>
Date: Mon, 11 Dec 2023 18:24:45 +0200
Subject: [PATCH 11/15] Try to fix CI - missing header

---
 .../test/std/containers/views/views.span/span.elem/at.pass.cpp  | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp b/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp
index 92484f2e4665d3..758de0f40e1091 100644
--- a/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp
@@ -19,9 +19,9 @@
 #include <span>
 #include <stdexcept>
 #include <string>
+#include <tuple>
 #include <utility>
 #include <vector>
-#include <iostream>
 
 #include "test_macros.h"
 

>From d9b2a08ed4cfc6a114af055cc69aed9276794b39 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Mon, 11 Dec 2023 21:09:56 +0200
Subject: [PATCH 12/15] Addressed comment

---
 libcxx/include/span | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/span b/libcxx/include/span
index 8c52b00e4ae967..24a0057680edee 100644
--- a/libcxx/include/span
+++ b/libcxx/include/span
@@ -347,9 +347,8 @@ public:
 
 #  if _LIBCPP_STD_VER >= 26
     _LIBCPP_HIDE_FROM_ABI constexpr reference at(size_type __index) const {
-      if (__index >= size()) {
+      if (__index >= size())
         std::__throw_out_of_range("span");
-      }
       return __data_[__index];
     }
 #  endif
@@ -523,9 +522,8 @@ public:
 
 #  if _LIBCPP_STD_VER >= 26
     _LIBCPP_HIDE_FROM_ABI constexpr reference at(size_type __index) const {
-      if (__index >= size()) {
+      if (__index >= size())
         std::__throw_out_of_range("span");
-      }
       return __data_[__index];
     }
 #  endif

>From 4ef25f7c9d78e42e9fb750ead52c9a30e9d484ea Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Sat, 16 Dec 2023 21:03:57 +0200
Subject: [PATCH 13/15] Removed bad file

---
 z_libcxx-dev-tools | 1 -
 1 file changed, 1 deletion(-)
 delete mode 160000 z_libcxx-dev-tools

diff --git a/z_libcxx-dev-tools b/z_libcxx-dev-tools
deleted file mode 160000
index 229818e01844e2..00000000000000
--- a/z_libcxx-dev-tools
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 229818e01844e2cad56818bf06703600071848c0

>From bf43de05db328c7c6093c7323114e12c797f67d9 Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Sat, 16 Dec 2023 21:08:12 +0200
Subject: [PATCH 14/15] Addressed comment

---
 .../test/std/containers/views/views.span/span.elem/at.pass.cpp  | 2 --
 1 file changed, 2 deletions(-)

diff --git a/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp b/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp
index 758de0f40e1091..d5876d406cf525 100644
--- a/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp
@@ -95,8 +95,6 @@ void test_exceptions() {
       std::ignore = arrSpan.at(arr.size() - 1);
       // pass
       assert(arrSpan.at(arr.size() - 1) == std::numeric_limits<int>::max());
-    } catch (const std::out_of_range&) {
-      assert(false);
     } catch (...) {
       assert(false);
     }

>From d279bff68f8a829fd0e75dc4f047b20f4171f19e Mon Sep 17 00:00:00 2001
From: Zingam <zingam at outlook.com>
Date: Sun, 17 Dec 2023 18:22:55 +0200
Subject: [PATCH 15/15] Addressed review comments

---
 .../containers/views/views.span/span.elem/at.pass.cpp  | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp b/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp
index d5876d406cf525..efe64222bddffa 100644
--- a/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.elem/at.pass.cpp
@@ -86,7 +86,7 @@ void test_exceptions() {
       assert(false);
     } catch (const std::out_of_range& e) {
       // pass
-      assert(e.what() == "span"s);
+      LIBCPP_ASSERT(e.what() == "span"s);
     } catch (...) {
       assert(false);
     }
@@ -109,7 +109,7 @@ void test_exceptions() {
       assert(false);
     } catch (const std::out_of_range& e) {
       // pass
-      assert(e.what() == "span"s);
+      LIBCPP_ASSERT(e.what() == "span"s);
     } catch (...) {
       assert(false);
     }
@@ -126,7 +126,7 @@ void test_exceptions() {
       assert(false);
     } catch (const std::out_of_range& e) {
       // pass
-      assert(e.what() == "span"s);
+      LIBCPP_ASSERT(e.what() == "span"s);
     } catch (...) {
       assert(false);
     }
@@ -134,8 +134,6 @@ void test_exceptions() {
     try {
       std::ignore = vecSpan.at(vec.size() - 1);
       assert(vecSpan.at(vec.size() - 1) == std::numeric_limits<int>::max());
-    } catch (const std::out_of_range& e) {
-      assert(false);
     } catch (...) {
       assert(false);
     }
@@ -150,7 +148,7 @@ void test_exceptions() {
       assert(false);
     } catch (const std::out_of_range& e) {
       // pass
-      assert(e.what() == "span"s);
+      LIBCPP_ASSERT(e.what() == "span"s);
     } catch (...) {
       assert(false);
     }



More information about the libc-commits mailing list