[libcxx-commits] [libcxx] [libc++][ranges] P3052R2: `view_interface::at()` (PR #205012)

via libcxx-commits libcxx-commits at lists.llvm.org
Thu Aug 27 06:16:23 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Hristo Hristov (H-G-Hristov)

<details>
<summary>Changes</summary>

Closes #<!-- -->204395

Implements https://wg21.link/P3052R2

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


12 Files Affected:

- (modified) libcxx/docs/FeatureTestMacroTable.rst (+1-1) 
- (modified) libcxx/docs/ReleaseNotes/24.rst (+1) 
- (modified) libcxx/docs/Status/Cxx29Papers.csv (+1-1) 
- (modified) libcxx/include/__ranges/view_interface.h (+26) 
- (modified) libcxx/include/version (+1-1) 
- (modified) libcxx/test/libcxx/ranges/range.utility/view.interface/nodiscard.verify.cpp (+7) 
- (modified) libcxx/test/libcxx/transitive_includes/cxx23.csv (+1) 
- (modified) libcxx/test/libcxx/transitive_includes/cxx26.csv (+1) 
- (modified) libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp (+5-11) 
- (modified) libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp (+5-11) 
- (modified) libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp (+49) 
- (modified) libcxx/utils/generate_feature_test_macro_components.py (-1) 


``````````diff
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index caf67d012db98..9c762989feeef 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -554,6 +554,6 @@ Status
     ---------------------------------------------------------- -----------------
     **C++29**
     ----------------------------------------------------------------------------
-    ``__cpp_lib_view_interface``                               *unimplemented*
+    ``__cpp_lib_view_interface``                               ``202606L``
     ========================================================== =================
 
diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index 9cf08b5732970..5019c597e0eb0 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -38,6 +38,7 @@ What's New in Libc++ 24.0.0?
 Implemented Papers
 ------------------
 
+- P3052R2: ``view_interface::at()`` (`Github <https://llvm.org/PR105185>`__)
 - P0493R5: Atomic minimum/maximum (`Github <https://llvm.org/PR105418>`__)
 - P3059R2: Making user-defined constructors of view iterators/sentinels private (`Github <https://github.com/llvm/llvm-project/issues/189599>`__)
 - P0792R14: ``function_ref`` : a type-erased callable reference (`Github <https://llvm.org/PR105376>`__)
diff --git a/libcxx/docs/Status/Cxx29Papers.csv b/libcxx/docs/Status/Cxx29Papers.csv
index 3c48fe89be33d..55bbadd8a6342 100644
--- a/libcxx/docs/Status/Cxx29Papers.csv
+++ b/libcxx/docs/Status/Cxx29Papers.csv
@@ -4,7 +4,7 @@
 "`P2414R12 <https://wg21.link/P2414R12>`__","Pointer lifetime-end zap proposed solutions","2026-06 (Brno)","","","`#204392 <https://github.com/llvm/llvm-project/issues/204392>`__","Voted as a Defect Report."
 "`P3319R6 <https://wg21.link/P3319R6>`__","Add an ``iota`` object for ``simd`` (and more)","2026-06 (Brno)","","","`#204393 <https://github.com/llvm/llvm-project/issues/204393>`__",""
 "`P3798R1 <https://wg21.link/P3798R1>`__","The unexpected in ``std::expected``","2026-06 (Brno)","","","`#204394 <https://github.com/llvm/llvm-project/issues/204394>`__",""
-"`P3052R2 <https://wg21.link/P3052R2>`__","``view_interface::at()``","2026-06 (Brno)","","","`#204395 <https://github.com/llvm/llvm-project/issues/204395>`__",""
+"`P3052R2 <https://wg21.link/P3052R2>`__","``view_interface::at()``","2026-06 (Brno)","|Complete|","24","`#204395 <https://github.com/llvm/llvm-project/issues/204395>`__",""
 "`P4206R0 <https://wg21.link/P4206R0>`__","Revert string support in ``std::constant_wrapper``","2026-06 (Brno)","|Complete|","23","`#203336 <https://github.com/llvm/llvm-project/issues/203336>`__","Applied as a Defect Report."
 "`P3395R6 <https://wg21.link/P3395R6>`__","Fix encoding issues and add a formatter for ``std::error_code``","2026-06 (Brno)","","","`#204396 <https://github.com/llvm/llvm-project/issues/204396>`__",""
 "`P3505R4 <https://wg21.link/P3505R4>`__","Fix the default floating-point representation in ``std::format``","2026-06 (Brno)","","","`#204397 <https://github.com/llvm/llvm-project/issues/204397>`__","To be applied as a Defect Report."
diff --git a/libcxx/include/__ranges/view_interface.h b/libcxx/include/__ranges/view_interface.h
index 96508c2be7277..c9f78966d1953 100644
--- a/libcxx/include/__ranges/view_interface.h
+++ b/libcxx/include/__ranges/view_interface.h
@@ -15,6 +15,7 @@
 #include <__concepts/same_as.h>
 #include <__config>
 #include <__iterator/concepts.h>
+#include <__iterator/distance.h>
 #include <__iterator/iterator_traits.h>
 #include <__iterator/prev.h>
 #include <__memory/pointer_traits.h>
@@ -25,6 +26,7 @@
 #include <__type_traits/is_class.h>
 #include <__type_traits/make_unsigned.h>
 #include <__type_traits/remove_cv.h>
+#include <stdexcept>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -159,6 +161,30 @@ class view_interface {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](range_difference_t<_RARange> __index) const {
     return ranges::begin(__derived())[__index];
   }
+
+#  if _LIBCPP_STD_VER >= 29
+
+  template <random_access_range _RARange = _Derived>
+    requires sized_range<_RARange> // freestanding-deleted
+  [[nodiscard]] constexpr decltype(auto) at(range_difference_t<_RARange> __index) {
+    if (__index < 0 || __index >= ranges::distance(__derived())) {
+      std::__throw_out_of_range(
+          "Precondition `0 <= __index < distance()` not satisfied. `.at(__index)` called with out-of-bounds index.");
+    }
+    return (*this)[__index];
+  }
+
+  template <random_access_range _RARange = const _Derived>
+    requires sized_range<_RARange> // freestanding-deleted
+  [[nodiscard]] constexpr decltype(auto) at(range_difference_t<_RARange> __index) const {
+    if (__index < 0 || __index >= ranges::distance(__derived())) {
+      std::__throw_out_of_range(
+          "Precondition `0 <= __index < distance()` not satisfied. `.at(__index)` called with out-of-bounds index.");
+    }
+    return (*this)[__index];
+  }
+
+#  endif // _LIBCPP_STD_VER >= 29
 };
 
 } // namespace ranges
diff --git a/libcxx/include/version b/libcxx/include/version
index a477e9c2802f6..a58a436bbf275 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -651,7 +651,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 #endif
 
 #if _LIBCPP_STD_VER >= 29
-// # define __cpp_lib_view_interface                       202606L
+# define __cpp_lib_view_interface                       202606L
 #endif
 
 #endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
diff --git a/libcxx/test/libcxx/ranges/range.utility/view.interface/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.utility/view.interface/nodiscard.verify.cpp
index a22c03f5d1a71..2bccc170aa727 100644
--- a/libcxx/test/libcxx/ranges/range.utility/view.interface/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/ranges/range.utility/view.interface/nodiscard.verify.cpp
@@ -67,4 +67,11 @@ void test() {
   v[Diff{0}];
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::as_const(v)[Diff{0}];
+
+#if TEST_STD_VER >= 29
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  v.at(Diff{0});
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(v).at(Diff{0});
+#endif
 }
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index a199c610e9f57..8c03f1c3c2251 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -11,6 +11,7 @@ algorithm iosfwd
 algorithm limits
 algorithm optional
 algorithm ratio
+algorithm stdexcept
 algorithm tuple
 algorithm version
 any cstdint
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index bd9227ca685d0..3f4daee66625a 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -10,6 +10,7 @@ algorithm initializer_list
 algorithm iosfwd
 algorithm limits
 algorithm ratio
+algorithm stdexcept
 algorithm tuple
 algorithm version
 any cstdint
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
index 4d6ed07bb5417..1b507cdafb33a 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
@@ -671,17 +671,11 @@
 #    error "__cpp_lib_ranges_zip should have the value 202110L in c++29"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_view_interface
-#      error "__cpp_lib_view_interface should be defined in c++29"
-#    endif
-#    if __cpp_lib_view_interface != 202606L
-#      error "__cpp_lib_view_interface should have the value 202606L in c++29"
-#    endif
-#  else
-#    ifdef __cpp_lib_view_interface
-#      error "__cpp_lib_view_interface should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_view_interface
+#    error "__cpp_lib_view_interface should be defined in c++29"
+#  endif
+#  if __cpp_lib_view_interface != 202606L
+#    error "__cpp_lib_view_interface should have the value 202606L in c++29"
 #  endif
 
 #endif // TEST_STD_VER > 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 b7ac29dec3a22..fc373b44cd500 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
@@ -10427,17 +10427,11 @@
 #    error "__cpp_lib_variant should have the value 202306L in c++29"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_view_interface
-#      error "__cpp_lib_view_interface should be defined in c++29"
-#    endif
-#    if __cpp_lib_view_interface != 202606L
-#      error "__cpp_lib_view_interface should have the value 202606L in c++29"
-#    endif
-#  else
-#    ifdef __cpp_lib_view_interface
-#      error "__cpp_lib_view_interface should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_view_interface
+#    error "__cpp_lib_view_interface should be defined in c++29"
+#  endif
+#  if __cpp_lib_view_interface != 202606L
+#    error "__cpp_lib_view_interface should have the value 202606L in c++29"
 #  endif
 
 #  ifndef __cpp_lib_void_t
diff --git a/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp b/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp
index 196abb0f2775c..ad871207c4c71 100644
--- a/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp
+++ b/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp
@@ -304,6 +304,50 @@ constexpr bool testSubscript() {
   return true;
 }
 
+#if TEST_STD_VER >= 29
+
+template <class T>
+concept AtInvocable = requires(T const& obj, std::size_t n) { obj.at(n); };
+
+constexpr bool testAt() {
+  static_assert(!AtInvocable<ForwardRange>);
+  static_assert(AtInvocable<RARange>);
+
+  RARange randomAccess;
+  assert(randomAccess.at(2) == 2);
+  assert(static_cast<RARange const&>(randomAccess).at(2) == 2);
+  randomAccess.at(2) = 3;
+  assert(randomAccess.at(2) == 3);
+
+#  ifndef TEST_HAS_NO_EXCEPTIONS
+  if (!std::is_constant_evaluated()) {
+    try {
+      TEST_IGNORE_NODISCARD randomAccess.at(0);
+      assert(true);
+    } catch (std::out_of_range const&) {
+      // pass
+      assert(false); // This should not be reached because index 0 is valid.
+    } catch (...) {
+      assert(false);
+    }
+
+    // Test that at() throws an exception when the index is out of range.
+    try {
+      TEST_IGNORE_NODISCARD randomAccess.at(10);
+      assert(false);
+    } catch (std::out_of_range const&) {
+      // pass
+    } catch (...) {
+      assert(false);
+    }
+  }
+#  endif
+
+  return true;
+}
+
+#endif // TEST_STD_VER >= 29
+
 template <class T>
 concept FrontInvocable = requires(T const& obj) { obj.front(); };
 
@@ -355,6 +399,11 @@ int main(int, char**) {
   testSubscript();
   static_assert(testSubscript());
 
+#if TEST_STD_VER >= 29
+  testAt();
+  static_assert(testAt());
+#endif
+
   testFrontBack();
   static_assert(testFrontBack());
 
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 4f56fd83fa202..e3683f4a78ba7 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1539,7 +1539,6 @@ def add_version_header(tc):
                 "c++29": 202606,  # P3052R2 view_interface::at()
             },
             "headers": ["ranges"],
-            "unimplemented": True,
         },
         {
             "name": "__cpp_lib_void_t",

``````````

</details>


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


More information about the libcxx-commits mailing list