[libcxx-commits] [libcxx] 9446182 - [libc++][ranges] implement `std::views::elements_view`
via libcxx-commits
libcxx-commits at lists.llvm.org
Sun Jan 15 09:36:41 PST 2023
Author: Hui Xie
Date: 2023-01-15T17:36:10Z
New Revision: 94461822c75d5080bf648f86552f7a59b76905c9
URL: https://github.com/llvm/llvm-project/commit/94461822c75d5080bf648f86552f7a59b76905c9
DIFF: https://github.com/llvm/llvm-project/commit/94461822c75d5080bf648f86552f7a59b76905c9.diff
LOG: [libc++][ranges] implement `std::views::elements_view`
`subrange` is also a `tuple-like`. To avoid the add entire `subrange` dependencies to `tuple-like`, we need forward declaration of `subrange`. However, the class template constraints of `subrange` currently requires `__iterator/concepts.h`, which requires `<concepts>`. The problem is that currently `tuple-like` is used in several different places, including libc++ extension for pair constructors. we don't want to add `<concepts>` to pair and other stuff. So this change also created several small headers that `subrange`'s declaration needed inside `__iterator/concepts/`
Differential Revision: https://reviews.llvm.org/D136268
Added:
libcxx/include/__fwd/subrange.h
libcxx/include/__ranges/elements_view.h
libcxx/include/__tuple_dir/pair_like.h
libcxx/include/__tuple_dir/tuple_like_ext.h
libcxx/test/libcxx/ranges/range.adaptors/range.elements/elements_view.no_unique_address.compile.pass.cpp
libcxx/test/libcxx/ranges/range.adaptors/range.elements/sentinel.no_unique_address.compile.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.elements/adaptor.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.elements/base.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.elements/begin.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.elements/borrowed.compile.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.elements/ctor.default.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.elements/ctor.view.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.elements/end.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.elements/general.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.elements/iterator/arithmetic.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.elements/iterator/compare.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.default.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.elements/iterator/decrement.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.elements/iterator/deref.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.elements/iterator/increment.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.elements/iterator/member_types.compile.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.elements/iterator/subscript.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.elements/range.concept.compile.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.default.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/equality.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/minus.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.elements/size.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.elements/types.h
Modified:
libcxx/docs/ReleaseNotes.rst
libcxx/docs/Status/Cxx20Issues.csv
libcxx/docs/Status/Cxx20Papers.csv
libcxx/docs/Status/Cxx2bIssues.csv
libcxx/docs/Status/SpaceshipProjects.csv
libcxx/include/CMakeLists.txt
libcxx/include/__fwd/get.h
libcxx/include/__ranges/subrange.h
libcxx/include/__tuple_dir/sfinae_helpers.h
libcxx/include/__tuple_dir/tuple_like.h
libcxx/include/module.modulemap.in
libcxx/include/ranges
libcxx/include/tuple
libcxx/test/libcxx/private_headers.verify.cpp
llvm/utils/gn/secondary/libcxx/include/BUILD.gn
Removed:
################################################################################
diff --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst
index 40c59517202fe..6f08d95c22a3e 100644
--- a/libcxx/docs/ReleaseNotes.rst
+++ b/libcxx/docs/ReleaseNotes.rst
@@ -66,6 +66,7 @@ Implemented Papers
- P0415R1 - ``constexpr`` for ``std::complex``
- P1208R6 - ``std::source_location``
- P0323R12 - ``std::expected``
+- P1035R7 - Input Range Adaptors
Improvements and New Features
-----------------------------
diff --git a/libcxx/docs/Status/Cxx20Issues.csv b/libcxx/docs/Status/Cxx20Issues.csv
index d023df674c23b..48bc936ba02c2 100644
--- a/libcxx/docs/Status/Cxx20Issues.csv
+++ b/libcxx/docs/Status/Cxx20Issues.csv
@@ -281,7 +281,7 @@
"`3373 <https://wg21.link/LWG3373>`__","``{to,from}_chars_result``\ and ``format_to_n_result``\ need the ""we really mean what we say"" wording","Prague","|Complete|","14.0","|format|"
"`3374 <https://wg21.link/LWG3374>`__","P0653 + P1006 should have made the other ``std::to_address``\ overload ``constexpr``\ ","Prague","|Complete|","12.0"
"`3375 <https://wg21.link/LWG3375>`__","``decay``\ in ``viewable_range``\ should be ``remove_cvref``\ ","Prague","|Complete|","15.0","|ranges|"
-"`3377 <https://wg21.link/LWG3377>`__","``elements_view::iterator``\ befriends a specialization of itself","Prague","","","|ranges|"
+"`3377 <https://wg21.link/LWG3377>`__","``elements_view::iterator``\ befriends a specialization of itself","Prague","|Nothing To Do|","","|ranges|"
"`3379 <https://wg21.link/LWG3379>`__","""``safe``\ "" in several library names is misleading","Prague","|Complete|","15.0","|ranges|"
"`3380 <https://wg21.link/LWG3380>`__","``common_type``\ and comparison categories","Prague","|Complete|","15.0","|spaceship|"
"`3381 <https://wg21.link/LWG3381>`__","``begin``\ and ``data``\ must agree for ``contiguous_range``\ ","Prague","|Nothing To Do|","","|ranges|"
diff --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv
index d03017b19c874..10fde392da460 100644
--- a/libcxx/docs/Status/Cxx20Papers.csv
+++ b/libcxx/docs/Status/Cxx20Papers.csv
@@ -108,7 +108,7 @@
"`P0784R7 <https://wg21.link/P0784R7>`__","CWG","More constexpr containers","Cologne","|Complete|","12.0"
"`P0980R1 <https://wg21.link/P0980R1>`__","LWG","Making std::string constexpr","Cologne","|Complete|","15.0"
"`P1004R2 <https://wg21.link/P1004R2>`__","LWG","Making std::vector constexpr","Cologne","|Complete|","15.0"
-"`P1035R7 <https://wg21.link/P1035R7>`__","LWG","Input Range Adaptors (mostly implemented, TODO: elements_view)","Cologne","|In Progress|","","|ranges|"
+"`P1035R7 <https://wg21.link/P1035R7>`__","LWG","Input Range Adaptors","Cologne","|Complete|","16.0","|ranges|"
"`P1065R2 <https://wg21.link/P1065R2>`__","LWG","Constexpr INVOKE","Cologne","|Complete|","12.0"
"`P1135R6 <https://wg21.link/P1135R6>`__","LWG","The C++20 Synchronization Library","Cologne","|Complete|","11.0"
"`P1207R4 <https://wg21.link/P1207R4>`__","LWG","Movability of Single-pass Iterators","Cologne","|Complete|","15.0","|ranges|"
@@ -182,7 +182,7 @@
"`P1981R0 <https://wg21.link/P1981R0>`__","LWG","Rename leap to leap_second","Prague","* *",""
"`P1982R0 <https://wg21.link/P1982R0>`__","LWG","Rename link to time_zone_link","Prague","* *",""
"`P1983R0 <https://wg21.link/P1983R0>`__","LWG","Wording for GB301, US296, US292, US291, and US283","Prague","|Complete|","15.0","|ranges|"
-"`P1994R1 <https://wg21.link/P1994R1>`__","LWG","elements_view needs its own sentinel","Prague","* *","","|ranges|"
+"`P1994R1 <https://wg21.link/P1994R1>`__","LWG","elements_view needs its own sentinel","Prague","Complete","16.0","|ranges|"
"`P2002R1 <https://wg21.link/P2002R1>`__","CWG","Defaulted comparison specification cleanups","Prague","* *",""
"`P2045R1 <https://wg21.link/P2045R1>`__","LWG","Missing Mandates for the standard library","Prague","* *",""
"`P2085R0 <https://wg21.link/P2085R0>`__","CWG","Consistent defaulted comparisons","Prague","* *",""
diff --git a/libcxx/docs/Status/Cxx2bIssues.csv b/libcxx/docs/Status/Cxx2bIssues.csv
index 2acfc800baf36..e92cfc3b60c42 100644
--- a/libcxx/docs/Status/Cxx2bIssues.csv
+++ b/libcxx/docs/Status/Cxx2bIssues.csv
@@ -22,7 +22,7 @@
"`3403 <https://wg21.link/LWG3403>`__","Domain of ``ranges::ssize(E)`` doesn't ``match ranges::size(E)``","November 2020","","","|ranges|"
"`3404 <https://wg21.link/LWG3404>`__","Finish removing subrange's conversions from pair-like","November 2020","","","|ranges|"
"`3405 <https://wg21.link/LWG3405>`__","``common_view``'s converting constructor is bad, too","November 2020","|Complete|","14.0","|ranges|"
-"`3406 <https://wg21.link/LWG3406>`__","``elements_view::begin()`` and ``elements_view::end()`` have incompatible constraints","November 2020","","","|ranges|"
+"`3406 <https://wg21.link/LWG3406>`__","``elements_view::begin()`` and ``elements_view::end()`` have incompatible constraints","November 2020","|Complete|","16.0","|ranges|"
"`3419 <https://wg21.link/LWG3419>`__","[algorithms.requirements]/15 doesn't reserve as many rights as it intends to","November 2020","|Nothing To Do|",""
"`3420 <https://wg21.link/LWG3420>`__","cpp17-iterator should check that the type looks like an iterator first","November 2020","|Complete|","14.0","|ranges|"
"`3421 <https://wg21.link/LWG3421>`__","Imperfect ADL emulation for boolean-testable","November 2020","|Nothing To Do|","","|ranges|"
@@ -53,11 +53,11 @@
"`3391 <https://wg21.link/LWG3391>`__","Problems with ``counted_iterator``/``move_iterator::base() const &``","February 2021","","","|ranges|"
"`3433 <https://wg21.link/LWG3433>`__","``subrange::advance(n)`` has UB when ``n < 0``","February 2021","|Complete|","14.0","|ranges|"
"`3490 <https://wg21.link/LWG3490>`__","``ranges::drop_while_view::begin()`` is missing a precondition","February 2021","|Nothing To Do|","","|ranges|"
-"`3492 <https://wg21.link/LWG3492>`__","Minimal improvements to ``elements_view::iterator``","February 2021","","","|ranges|"
+"`3492 <https://wg21.link/LWG3492>`__","Minimal improvements to ``elements_view::iterator``","February 2021","|Complete|","16.0","|ranges|"
"`3494 <https://wg21.link/LWG3494>`__","Allow ranges to be conditionally borrowed","February 2021","Superseded by `P2017R1 <https://wg21.link/P2017R1>`__","","|ranges|"
"`3495 <https://wg21.link/LWG3495>`__","``constexpr launder`` makes pointers to inactive members of unions usable","February 2021","|Nothing To Do|",""
"`3500 <https://wg21.link/LWG3500>`__","``join_view::iterator::operator->()`` is bogus","February 2021","|Complete|","14.0","|ranges|"
-"`3502 <https://wg21.link/LWG3502>`__","``elements_view`` should not be allowed to return dangling reference","February 2021","","","|ranges|"
+"`3502 <https://wg21.link/LWG3502>`__","``elements_view`` should not be allowed to return dangling reference","February 2021","|Complete|","16.0","|ranges|"
"`3505 <https://wg21.link/LWG3505>`__","``split_view::outer-iterator::operator++`` misspecified","February 2021","","","|ranges|"
"","","","","",""
`2774 <https://wg21.link/LWG2774>`__,"``std::function`` construction vs assignment","June 2021","",""
diff --git a/libcxx/docs/Status/SpaceshipProjects.csv b/libcxx/docs/Status/SpaceshipProjects.csv
index dec2a89f9a108..f732ec722115b 100644
--- a/libcxx/docs/Status/SpaceshipProjects.csv
+++ b/libcxx/docs/Status/SpaceshipProjects.csv
@@ -51,7 +51,7 @@ Section,Description,Dependencies,Assignee,Complete
| `[counted.iter.cmp] <https://wg21.link/counted.iter.cmp>`_,| counted_iterator,None,Unassigned,|Not Started|
| `[range.iota.iterator] <https://wg21.link/range.iota.iterator>`_,| `ranges::iota_view::iterator <https://reviews.llvm.org/D110774>`_,[concepts.cmp],Arthur O'Dwyer,|Complete|
| `[range.transform.iterator] <https://wg21.link/range.transform.iterator>`_,| `ranges::transform_view::iterator <https://reviews.llvm.org/D110774>`_,[concepts.cmp],Arthur O'Dwyer,|Complete|
-| `[range.elements.iterator] <https://wg21.link/range.elements.iterator>`_,| ranges::elements_view::iterator,[concepts.cmp],Unassigned,|Not Started|
+| `[range.elements.iterator] <https://wg21.link/range.elements.iterator>`_,| ranges::elements_view::iterator,[concepts.cmp],Hui Xie,|Complete|
| `[time.duration.comparisons] <https://wg21.link/time.duration.comparisons>`_, "chrono::duration", None, Mark de Wever, |Not Started|
| `[time.point.comparisons] <https://wg21.link/time.point.comparisons>`_, "chrono::time_point", None, Mark de Wever, |Not Started|
"| `[time.cal.day.nonmembers] <https://wg21.link/time.cal.day.nonmembers>`_
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 06c5eb16da1c6..ea52f205d4980 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -364,6 +364,7 @@ set(files
__fwd/span.h
__fwd/string.h
__fwd/string_view.h
+ __fwd/subrange.h
__fwd/tuple.h
__hash_table
__ios/fpos.h
@@ -503,6 +504,7 @@ set(files
__ranges/data.h
__ranges/drop_view.h
__ranges/drop_while_view.h
+ __ranges/elements_view.h
__ranges/empty.h
__ranges/empty_view.h
__ranges/enable_borrowed_range.h
@@ -554,10 +556,12 @@ set(files
__tree
__tuple_dir/apply_cv.h
__tuple_dir/make_tuple_types.h
+ __tuple_dir/pair_like.h
__tuple_dir/sfinae_helpers.h
__tuple_dir/tuple_element.h
__tuple_dir/tuple_indices.h
__tuple_dir/tuple_like.h
+ __tuple_dir/tuple_like_ext.h
__tuple_dir/tuple_size.h
__tuple_dir/tuple_types.h
__type_traits/add_const.h
diff --git a/libcxx/include/__fwd/get.h b/libcxx/include/__fwd/get.h
index 98758ebca0df2..ec1fab4602d7f 100644
--- a/libcxx/include/__fwd/get.h
+++ b/libcxx/include/__fwd/get.h
@@ -9,9 +9,11 @@
#ifndef _LIBCPP___FWD_GET_H
#define _LIBCPP___FWD_GET_H
+#include <__concepts/copyable.h>
#include <__config>
#include <__fwd/array.h>
#include <__fwd/pair.h>
+#include <__fwd/subrange.h>
#include <__fwd/tuple.h>
#include <__tuple_dir/tuple_element.h>
#include <cstddef>
@@ -90,6 +92,24 @@ const _Tp&&
get(const array<_Tp, _Size>&&) _NOEXCEPT;
#endif
+#if _LIBCPP_STD_VER >= 20
+
+namespace ranges {
+
+template <size_t _Index, class _Iter, class _Sent, subrange_kind _Kind>
+ requires((_Index == 0 && copyable<_Iter>) || _Index == 1)
+_LIBCPP_HIDE_FROM_ABI constexpr auto get(const subrange<_Iter, _Sent, _Kind>& __subrange);
+
+template <size_t _Index, class _Iter, class _Sent, subrange_kind _Kind>
+ requires(_Index < 2)
+_LIBCPP_HIDE_FROM_ABI constexpr auto get(subrange<_Iter, _Sent, _Kind>&& __subrange);
+
+} // namespace ranges
+
+using ranges::get;
+
+#endif // _LIBCPP_STD_VER >= 20
+
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP___FWD_GET_H
diff --git a/libcxx/include/__fwd/subrange.h b/libcxx/include/__fwd/subrange.h
new file mode 100644
index 0000000000000..8f7239247eff1
--- /dev/null
+++ b/libcxx/include/__fwd/subrange.h
@@ -0,0 +1,38 @@
+//===---------------------------------------------------------------------===//
+//
+// 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
+//
+//===---------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___FWD_SUBRANGE_H
+#define _LIBCPP___FWD_SUBRANGE_H
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+#if _LIBCPP_STD_VER >= 20
+
+#include <__iterator/concepts.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+namespace ranges {
+
+enum class _LIBCPP_ENUM_VIS subrange_kind : bool { unsized, sized };
+
+template <input_or_output_iterator _Iter, sentinel_for<_Iter> _Sent, subrange_kind _Kind>
+ requires(_Kind == subrange_kind::sized || !sized_sentinel_for<_Sent, _Iter>)
+class _LIBCPP_TEMPLATE_VIS subrange;
+
+} // namespace ranges
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 20
+
+#endif // _LIBCPP___FWD_SUBRANGE_H
diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h
new file mode 100644
index 0000000000000..3afd6ddbe8f2d
--- /dev/null
+++ b/libcxx/include/__ranges/elements_view.h
@@ -0,0 +1,423 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___RANGES_ELEMENTS_VIEW_H
+#define _LIBCPP___RANGES_ELEMENTS_VIEW_H
+
+#include <__compare/three_way_comparable.h>
+#include <__concepts/constructible.h>
+#include <__concepts/convertible_to.h>
+#include <__concepts/derived_from.h>
+#include <__concepts/equality_comparable.h>
+#include <__config>
+#include <__fwd/get.h>
+#include <__iterator/concepts.h>
+#include <__iterator/iterator_traits.h>
+#include <__ranges/access.h>
+#include <__ranges/all.h>
+#include <__ranges/concepts.h>
+#include <__ranges/enable_borrowed_range.h>
+#include <__ranges/range_adaptor.h>
+#include <__ranges/size.h>
+#include <__ranges/view_interface.h>
+#include <__tuple_dir/tuple_element.h>
+#include <__tuple_dir/tuple_like.h>
+#include <__tuple_dir/tuple_size.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/maybe_const.h>
+#include <__type_traits/remove_cv.h>
+#include <__type_traits/remove_cvref.h>
+#include <__type_traits/remove_reference.h>
+#include <__utility/declval.h>
+#include <__utility/forward.h>
+#include <__utility/move.h>
+#include <cstddef>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20
+
+namespace ranges {
+
+template <class _View, size_t _Np, bool _Const>
+class __elements_view_iterator;
+
+template <class _View, size_t _Np, bool _Const>
+class __elements_view_sentinel;
+
+template <class _Tp, size_t _Np>
+concept __has_tuple_element = __tuple_like<_Tp> && _Np < tuple_size<_Tp>::value;
+
+template <class _Tp, size_t _Np>
+concept __returnable_element = is_reference_v<_Tp> || move_constructible<tuple_element_t<_Np, _Tp>>;
+
+template <input_range _View, size_t _Np>
+ requires view<_View> && __has_tuple_element<range_value_t<_View>, _Np> &&
+ __has_tuple_element<remove_reference_t<range_reference_t<_View>>, _Np> &&
+ __returnable_element<range_reference_t<_View>, _Np>
+class elements_view : public view_interface<elements_view<_View, _Np>> {
+public:
+ _LIBCPP_HIDE_FROM_ABI elements_view()
+ requires default_initializable<_View>
+ = default;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit elements_view(_View __base) : __base_(std::move(__base)) {}
+
+ _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+ requires copy_constructible<_View>
+ {
+ return __base_;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr auto begin()
+ requires(!__simple_view<_View>)
+ {
+ return __iterator</*_Const=*/false>(ranges::begin(__base_));
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+ requires range<const _View>
+ {
+ return __iterator</*_Const=*/true>(ranges::begin(__base_));
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+ requires(!__simple_view<_View> && !common_range<_View>)
+ {
+ return __sentinel</*_Const=*/false>{ranges::end(__base_)};
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+ requires(!__simple_view<_View> && common_range<_View>)
+ {
+ return __iterator</*_Const=*/false>{ranges::end(__base_)};
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+ requires range<const _View>
+ {
+ return __sentinel</*_Const=*/true>{ranges::end(__base_)};
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+ requires common_range<const _View>
+ {
+ return __iterator</*_Const=*/true>{ranges::end(__base_)};
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+ requires sized_range<_View>
+ {
+ return ranges::size(__base_);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+ requires sized_range<const _View>
+ {
+ return ranges::size(__base_);
+ }
+
+private:
+ template <bool _Const>
+ using __iterator = __elements_view_iterator<_View, _Np, _Const>;
+
+ template <bool _Const>
+ using __sentinel = __elements_view_sentinel<_View, _Np, _Const>;
+
+ _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View();
+};
+
+template <class, size_t>
+struct __elements_view_iterator_category_base {};
+
+template <forward_range _Base, size_t _Np>
+struct __elements_view_iterator_category_base<_Base, _Np> {
+ static consteval auto __get_iterator_category() {
+ using _Result = decltype(std::get<_Np>(*std::declval<iterator_t<_Base>>()));
+ using _Cat = typename iterator_traits<iterator_t<_Base>>::iterator_category;
+
+ if constexpr (!is_lvalue_reference_v<_Result>) {
+ return input_iterator_tag{};
+ } else if constexpr (derived_from<_Cat, random_access_iterator_tag>) {
+ return random_access_iterator_tag{};
+ } else {
+ return _Cat{};
+ }
+ }
+
+ using iterator_category = decltype(__get_iterator_category());
+};
+
+template <class _View, size_t _Np, bool _Const>
+class __elements_view_iterator : public __elements_view_iterator_category_base<__maybe_const<_Const, _View>, _Np> {
+ template <class, size_t, bool >
+ friend class __elements_view_iterator;
+
+ template <class, size_t, bool >
+ friend class __elements_view_sentinel;
+
+ using _Base = __maybe_const<_Const, _View>;
+
+ iterator_t<_Base> __current_ = iterator_t<_Base>();
+
+ _LIBCPP_HIDE_FROM_ABI static constexpr decltype(auto) __get_element(const iterator_t<_Base>& __i) {
+ if constexpr (is_reference_v<range_reference_t<_Base>>) {
+ return std::get<_Np>(*__i);
+ } else {
+ using _Element = remove_cv_t<tuple_element_t<_Np, range_reference_t<_Base>>>;
+ return static_cast<_Element>(std::get<_Np>(*__i));
+ }
+ }
+
+ static consteval auto __get_iterator_concept() {
+ if constexpr (random_access_range<_Base>) {
+ return random_access_iterator_tag{};
+ } else if constexpr (bidirectional_range<_Base>) {
+ return bidirectional_iterator_tag{};
+ } else if constexpr (forward_range<_Base>) {
+ return forward_iterator_tag{};
+ } else {
+ return input_iterator_tag{};
+ }
+ }
+
+public:
+ using iterator_concept = decltype(__get_iterator_concept());
+ using value_type = remove_cvref_t<tuple_element_t<_Np, range_value_t<_Base>>>;
+ using
diff erence_type = range_
diff erence_t<_Base>;
+
+ _LIBCPP_HIDE_FROM_ABI __elements_view_iterator()
+ requires default_initializable<iterator_t<_Base>>
+ = default;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __elements_view_iterator(iterator_t<_Base> __current)
+ : __current_(std::move(__current)) {}
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __elements_view_iterator(__elements_view_iterator<_View, _Np, !_Const> __i)
+ requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
+ : __current_(std::move(__i.__current_)) {}
+
+ _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept { return __current_; }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() && { return std::move(__current_); }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return __get_element(__current_); }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __elements_view_iterator& operator++() {
+ ++__current_;
+ return *this;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { ++__current_; }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __elements_view_iterator operator++(int)
+ requires forward_range<_Base>
+ {
+ auto temp = *this;
+ ++__current_;
+ return temp;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __elements_view_iterator& operator--()
+ requires bidirectional_range<_Base>
+ {
+ --__current_;
+ return *this;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __elements_view_iterator operator--(int)
+ requires bidirectional_range<_Base>
+ {
+ auto temp = *this;
+ --__current_;
+ return temp;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __elements_view_iterator& operator+=(
diff erence_type __n)
+ requires random_access_range<_Base>
+ {
+ __current_ += __n;
+ return *this;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __elements_view_iterator& operator-=(
diff erence_type __n)
+ requires random_access_range<_Base>
+ {
+ __current_ -= __n;
+ return *this;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](
diff erence_type __n) const
+ requires random_access_range<_Base>
+ {
+ return __get_element(__current_ + __n);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+ operator==(const __elements_view_iterator& __x, const __elements_view_iterator& __y)
+ requires equality_comparable<iterator_t<_Base>>
+ {
+ return __x.__current_ == __y.__current_;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+ operator<(const __elements_view_iterator& __x, const __elements_view_iterator& __y)
+ requires random_access_range<_Base>
+ {
+ return __x.__current_ < __y.__current_;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+ operator>(const __elements_view_iterator& __x, const __elements_view_iterator& __y)
+ requires random_access_range<_Base>
+ {
+ return __y < __x;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+ operator<=(const __elements_view_iterator& __x, const __elements_view_iterator& __y)
+ requires random_access_range<_Base>
+ {
+ return !(__y < __x);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+ operator>=(const __elements_view_iterator& __x, const __elements_view_iterator& __y)
+ requires random_access_range<_Base>
+ {
+ return !(__x < __y);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr auto
+ operator<=>(const __elements_view_iterator& __x, const __elements_view_iterator& __y)
+ requires random_access_range<_Base> && three_way_comparable<iterator_t<_Base>>
+ {
+ return __x.__current_ <=> __y.__current_;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr __elements_view_iterator
+ operator+(const __elements_view_iterator& __x,
diff erence_type __y)
+ requires random_access_range<_Base>
+ {
+ return __elements_view_iterator{__x} += __y;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr __elements_view_iterator
+ operator+(
diff erence_type __x, const __elements_view_iterator& __y)
+ requires random_access_range<_Base>
+ {
+ return __y + __x;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr __elements_view_iterator
+ operator-(const __elements_view_iterator& __x,
diff erence_type __y)
+ requires random_access_range<_Base>
+ {
+ return __elements_view_iterator{__x} -= __y;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr
diff erence_type
+ operator-(const __elements_view_iterator& __x, const __elements_view_iterator& __y)
+ requires sized_sentinel_for<iterator_t<_Base>, iterator_t<_Base>>
+ {
+ return __x.__current_ - __y.__current_;
+ }
+};
+
+template <class _View, size_t _Np, bool _Const>
+class __elements_view_sentinel {
+private:
+ using _Base = __maybe_const<_Const, _View>;
+ _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_Base> __end_ = sentinel_t<_Base>();
+
+ template <class, size_t, bool >
+ friend class __elements_view_sentinel;
+
+ template <bool _AnyConst>
+ _LIBCPP_HIDE_FROM_ABI static constexpr decltype(auto)
+ __get_current(const __elements_view_iterator<_View, _Np, _AnyConst>& __iter) {
+ return (__iter.__current_);
+ }
+
+public:
+ _LIBCPP_HIDE_FROM_ABI __elements_view_sentinel() = default;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __elements_view_sentinel(sentinel_t<_Base> __end)
+ : __end_(std::move(__end)) {}
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __elements_view_sentinel(__elements_view_sentinel<_View, _Np, !_Const> __other)
+ requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
+ : __end_(std::move(__other.__end_)) {}
+
+ _LIBCPP_HIDE_FROM_ABI constexpr sentinel_t<_Base> base() const { return __end_; }
+
+ template <bool _OtherConst>
+ requires sentinel_for<sentinel_t<_Base>, iterator_t<__maybe_const<_OtherConst, _View>>>
+ _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+ operator==(const __elements_view_iterator<_View, _Np, _OtherConst>& __x, const __elements_view_sentinel& __y) {
+ return __get_current(__x) == __y.__end_;
+ }
+
+ template <bool _OtherConst>
+ requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<__maybe_const<_OtherConst, _View>>>
+ _LIBCPP_HIDE_FROM_ABI friend constexpr range_
diff erence_t<__maybe_const<_OtherConst, _View>>
+ operator-(const __elements_view_iterator<_View, _Np, _OtherConst>& __x, const __elements_view_sentinel& __y) {
+ return __get_current(__x) - __y.__end_;
+ }
+
+ template <bool _OtherConst>
+ requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<__maybe_const<_OtherConst, _View>>>
+ _LIBCPP_HIDE_FROM_ABI friend constexpr range_
diff erence_t<__maybe_const<_OtherConst, _View>>
+ operator-(const __elements_view_sentinel& __x, const __elements_view_iterator<_View, _Np, _OtherConst>& __y) {
+ return __x.__end_ - __get_current(__y);
+ }
+};
+
+template <class _Tp, size_t _Np>
+inline constexpr bool enable_borrowed_range<elements_view<_Tp, _Np>> = enable_borrowed_range<_Tp>;
+
+template <class _Tp>
+using keys_view = elements_view<_Tp, 0>;
+template <class _Tp>
+using values_view = elements_view<_Tp, 1>;
+
+namespace views {
+namespace __elements {
+
+template <size_t _Np>
+struct __fn : __range_adaptor_closure<__fn<_Np>> {
+ template <class _Range>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Range&& __range) const
+ /**/ noexcept(noexcept(elements_view<all_t<_Range&&>, _Np>(std::forward<_Range>(__range))))
+ /*------*/ -> decltype(elements_view<all_t<_Range&&>, _Np>(std::forward<_Range>(__range))) {
+ /*-------------*/ return elements_view<all_t<_Range&&>, _Np>(std::forward<_Range>(__range));
+ }
+};
+} // namespace __elements
+
+inline namespace __cpo {
+template <size_t _Np>
+inline constexpr auto elements = __elements::__fn<_Np>{};
+inline constexpr auto keys = elements<0>;
+inline constexpr auto values = elements<1>;
+} // namespace __cpo
+} // namespace views
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 20
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___RANGES_ELEMENTS_VIEW_H
diff --git a/libcxx/include/__ranges/subrange.h b/libcxx/include/__ranges/subrange.h
index f47db6b843f0a..2d9e4cc7e55e2 100644
--- a/libcxx/include/__ranges/subrange.h
+++ b/libcxx/include/__ranges/subrange.h
@@ -18,6 +18,7 @@
#include <__concepts/
diff erent_from.h>
#include <__config>
#include <__fwd/get.h>
+#include <__fwd/subrange.h>
#include <__iterator/advance.h>
#include <__iterator/concepts.h>
#include <__iterator/incrementable_traits.h>
@@ -28,6 +29,7 @@
#include <__ranges/enable_borrowed_range.h>
#include <__ranges/size.h>
#include <__ranges/view_interface.h>
+#include <__tuple_dir/pair_like.h>
#include <__tuple_dir/tuple_element.h>
#include <__tuple_dir/tuple_size.h>
#include <__type_traits/conditional.h>
@@ -59,17 +61,6 @@ namespace ranges {
convertible_to<_From, _To> &&
!__uses_nonqualification_pointer_conversion<decay_t<_From>, decay_t<_To>>;
- template<class _Tp>
- concept __pair_like =
- !is_reference_v<_Tp> && requires(_Tp __t) {
- typename tuple_size<_Tp>::type; // Ensures `tuple_size<T>` is complete.
- requires derived_from<tuple_size<_Tp>, integral_constant<size_t, 2>>;
- typename tuple_element_t<0, remove_const_t<_Tp>>;
- typename tuple_element_t<1, remove_const_t<_Tp>>;
- { std::get<0>(__t) } -> convertible_to<const tuple_element_t<0, _Tp>&>;
- { std::get<1>(__t) } -> convertible_to<const tuple_element_t<1, _Tp>&>;
- };
-
template<class _Pair, class _Iter, class _Sent>
concept __pair_like_convertible_from =
!range<_Pair> && __pair_like<_Pair> &&
@@ -77,8 +68,6 @@ namespace ranges {
__convertible_to_non_slicing<_Iter, tuple_element_t<0, _Pair>> &&
convertible_to<_Sent, tuple_element_t<1, _Pair>>;
- enum class _LIBCPP_ENUM_VIS subrange_kind : bool { unsized, sized };
-
template<input_or_output_iterator _Iter, sentinel_for<_Iter> _Sent = _Iter,
subrange_kind _Kind = sized_sentinel_for<_Sent, _Iter>
? subrange_kind::sized
diff --git a/libcxx/include/__tuple_dir/pair_like.h b/libcxx/include/__tuple_dir/pair_like.h
new file mode 100644
index 0000000000000..87407ad95b6ff
--- /dev/null
+++ b/libcxx/include/__tuple_dir/pair_like.h
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___TUPLE_PAIR_LIKE_H
+#define _LIBCPP___TUPLE_PAIR_LIKE_H
+
+#include <__config>
+#include <__tuple_dir/tuple_like.h>
+#include <__tuple_dir/tuple_size.h>
+#include <__type_traits/remove_cvref.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20
+
+template <class _Tp>
+concept __pair_like = __tuple_like<_Tp> && tuple_size<remove_cvref_t<_Tp>>::value == 2;
+
+#endif // _LIBCPP_STD_VER >= 20
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___TUPLE_PAIR_LIKE_H
diff --git a/libcxx/include/__tuple_dir/sfinae_helpers.h b/libcxx/include/__tuple_dir/sfinae_helpers.h
index fde53419d07b9..fcd65a0817be2 100644
--- a/libcxx/include/__tuple_dir/sfinae_helpers.h
+++ b/libcxx/include/__tuple_dir/sfinae_helpers.h
@@ -13,7 +13,7 @@
#include <__fwd/tuple.h>
#include <__tuple_dir/make_tuple_types.h>
#include <__tuple_dir/tuple_element.h>
-#include <__tuple_dir/tuple_like.h>
+#include <__tuple_dir/tuple_like_ext.h>
#include <__tuple_dir/tuple_size.h>
#include <__tuple_dir/tuple_types.h>
#include <__type_traits/enable_if.h>
@@ -58,8 +58,8 @@ struct __tuple_sfinae_base {
// __tuple_convertible
-template <class _Tp, class _Up, bool = __tuple_like<__libcpp_remove_reference_t<_Tp> >::value,
- bool = __tuple_like<_Up>::value>
+template <class _Tp, class _Up, bool = __tuple_like_ext<__libcpp_remove_reference_t<_Tp> >::value,
+ bool = __tuple_like_ext<_Up>::value>
struct __tuple_convertible
: public false_type {};
@@ -73,8 +73,8 @@ struct __tuple_convertible<_Tp, _Up, true, true>
// __tuple_constructible
-template <class _Tp, class _Up, bool = __tuple_like<__libcpp_remove_reference_t<_Tp> >::value,
- bool = __tuple_like<_Up>::value>
+template <class _Tp, class _Up, bool = __tuple_like_ext<__libcpp_remove_reference_t<_Tp> >::value,
+ bool = __tuple_like_ext<_Up>::value>
struct __tuple_constructible
: public false_type {};
@@ -88,8 +88,8 @@ struct __tuple_constructible<_Tp, _Up, true, true>
// __tuple_assignable
-template <class _Tp, class _Up, bool = __tuple_like<__libcpp_remove_reference_t<_Tp> >::value,
- bool = __tuple_like<_Up>::value>
+template <class _Tp, class _Up, bool = __tuple_like_ext<__libcpp_remove_reference_t<_Tp> >::value,
+ bool = __tuple_like_ext<_Up>::value>
struct __tuple_assignable
: public false_type {};
@@ -117,7 +117,7 @@ struct __tuple_like_with_size_imp<true, _SizeTrait, _Expected>
template <class _Tuple, size_t _ExpectedSize, class _RawTuple = __libcpp_remove_reference_t<_Tuple> >
using __tuple_like_with_size _LIBCPP_NODEBUG = __tuple_like_with_size_imp<
- __tuple_like<_RawTuple>::value,
+ __tuple_like_ext<_RawTuple>::value,
tuple_size<_RawTuple>, _ExpectedSize
>;
diff --git a/libcxx/include/__tuple_dir/tuple_like.h b/libcxx/include/__tuple_dir/tuple_like.h
index 327287786e3b2..dab395be616b7 100644
--- a/libcxx/include/__tuple_dir/tuple_like.h
+++ b/libcxx/include/__tuple_dir/tuple_like.h
@@ -12,9 +12,10 @@
#include <__config>
#include <__fwd/array.h>
#include <__fwd/pair.h>
+#include <__fwd/subrange.h>
#include <__fwd/tuple.h>
-#include <__tuple_dir/tuple_types.h>
#include <__type_traits/integral_constant.h>
+#include <__type_traits/remove_cvref.h>
#include <cstddef>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -23,21 +24,27 @@
_LIBCPP_BEGIN_NAMESPACE_STD
-template <class _Tp> struct __tuple_like : false_type {};
+#if _LIBCPP_STD_VER >= 20
-template <class _Tp> struct __tuple_like<const _Tp> : public __tuple_like<_Tp> {};
-template <class _Tp> struct __tuple_like<volatile _Tp> : public __tuple_like<_Tp> {};
-template <class _Tp> struct __tuple_like<const volatile _Tp> : public __tuple_like<_Tp> {};
+template <class _Tp>
+struct __tuple_like_impl : false_type {};
-#ifndef _LIBCPP_CXX03_LANG
-template <class... _Tp> struct __tuple_like<tuple<_Tp...> > : true_type {};
-#endif
+template <class... _Tp>
+struct __tuple_like_impl<tuple<_Tp...> > : true_type {};
+
+template <class _T1, class _T2>
+struct __tuple_like_impl<pair<_T1, _T2> > : true_type {};
+
+template <class _Tp, size_t _Size>
+struct __tuple_like_impl<array<_Tp, _Size> > : true_type {};
-template <class _T1, class _T2> struct __tuple_like<pair<_T1, _T2> > : true_type {};
+template <class _Ip, class _Sp, ranges::subrange_kind _Kp>
+struct __tuple_like_impl<ranges::subrange<_Ip, _Sp, _Kp> > : true_type {};
-template <class _Tp, size_t _Size> struct __tuple_like<array<_Tp, _Size> > : true_type {};
+template <class _Tp>
+concept __tuple_like = __tuple_like_impl<remove_cvref_t<_Tp>>::value;
-template <class... _Tp> struct __tuple_like<__tuple_types<_Tp...> > : true_type {};
+#endif // _LIBCPP_STD_VER >= 20
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__tuple_dir/tuple_like_ext.h b/libcxx/include/__tuple_dir/tuple_like_ext.h
new file mode 100644
index 0000000000000..bf9869611273f
--- /dev/null
+++ b/libcxx/include/__tuple_dir/tuple_like_ext.h
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___TUPLE_TUPLE_LIKE_EXT_H
+#define _LIBCPP___TUPLE_TUPLE_LIKE_EXT_H
+
+#include <__config>
+#include <__fwd/array.h>
+#include <__fwd/pair.h>
+#include <__fwd/tuple.h>
+#include <__tuple_dir/tuple_types.h>
+#include <__type_traits/integral_constant.h>
+#include <cstddef>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Tp> struct __tuple_like_ext : false_type {};
+
+template <class _Tp> struct __tuple_like_ext<const _Tp> : public __tuple_like_ext<_Tp> {};
+template <class _Tp> struct __tuple_like_ext<volatile _Tp> : public __tuple_like_ext<_Tp> {};
+template <class _Tp> struct __tuple_like_ext<const volatile _Tp> : public __tuple_like_ext<_Tp> {};
+
+#ifndef _LIBCPP_CXX03_LANG
+template <class... _Tp> struct __tuple_like_ext<tuple<_Tp...> > : true_type {};
+#endif
+
+template <class _T1, class _T2> struct __tuple_like_ext<pair<_T1, _T2> > : true_type {};
+
+template <class _Tp, size_t _Size> struct __tuple_like_ext<array<_Tp, _Size> > : true_type {};
+
+template <class... _Tp> struct __tuple_like_ext<__tuple_types<_Tp...> > : true_type {};
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___TUPLE_TUPLE_LIKE_EXT_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index d0cb522cc89ae..5d4cf53aa334e 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1229,6 +1229,7 @@ module std [system] {
module data { private header "__ranges/data.h" }
module drop_view { private header "__ranges/drop_view.h" }
module drop_while_view { private header "__ranges/drop_while_view.h" }
+ module elements_view { private header "__ranges/elements_view.h" }
module empty { private header "__ranges/empty.h" }
module empty_view { private header "__ranges/empty_view.h" }
module enable_borrowed_range { private header "__ranges/enable_borrowed_range.h" }
@@ -1250,7 +1251,11 @@ module std [system] {
module reverse_view { private header "__ranges/reverse_view.h" }
module single_view { private header "__ranges/single_view.h" }
module size { private header "__ranges/size.h" }
- module subrange { private header "__ranges/subrange.h" }
+ module subrange {
+ private header "__ranges/subrange.h"
+
+ module subrange_fwd { private header "__fwd/subrange.h" }
+ }
module take_view { private header "__ranges/take_view.h" }
module take_while_view { private header "__ranges/take_while_view.h" }
module transform_view {
@@ -1365,11 +1370,13 @@ module std [system] {
module apply_cv { private header "__tuple_dir/apply_cv.h" }
module get_fwd { private header "__fwd/get.h" }
module make_tuple_types { private header "__tuple_dir/make_tuple_types.h" }
+ module pair_like { private header "__tuple_dir/pair_like.h" }
module sfinae_helpers { private header "__tuple_dir/sfinae_helpers.h" }
module tuple_element { private header "__tuple_dir/tuple_element.h" }
module tuple_fwd { private header "__fwd/tuple.h" }
module tuple_indices { private header "__tuple_dir/tuple_indices.h" }
module tuple_like { private header "__tuple_dir/tuple_like.h" }
+ module tuple_like_ext { private header "__tuple_dir/tuple_like_ext.h" }
module tuple_size { private header "__tuple_dir/tuple_size.h" }
module tuple_types { private header "__tuple_dir/tuple_types.h" }
}
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 5928efa6027ec..db601d48c7f10 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -115,6 +115,27 @@ namespace std::ranges {
template<range R>
using borrowed_subrange_t = see below;
+ // [range.elements], elements view
+ template<input_range V, size_t N>
+ requires see below
+ class elements_view;
+
+ template<class T, size_t N>
+ inline constexpr bool enable_borrowed_range<elements_view<T, N>> =
+ enable_borrowed_range<T>;
+
+ template<class R>
+ using keys_view = elements_view<R, 0>;
+ template<class R>
+ using values_view = elements_view<R, 1>;
+
+ namespace views {
+ template<size_t N>
+ inline constexpr unspecified elements = unspecified;
+ inline constexpr auto keys = elements<0>;
+ inline constexpr auto values = elements<1>;
+ }
+
// [range.empty], empty view
template<class T>
requires is_object_v<T>
@@ -316,6 +337,7 @@ namespace std {
#include <__ranges/data.h>
#include <__ranges/drop_view.h>
#include <__ranges/drop_while_view.h>
+#include <__ranges/elements_view.h>
#include <__ranges/empty.h>
#include <__ranges/empty_view.h>
#include <__ranges/enable_borrowed_range.h>
diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index b75d00c4e431b..b0616bd94da53 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -1677,7 +1677,7 @@ struct __tuple_cat_return_1<tuple<_Types...>, true, _Tuple0, _Tuple1, _Tuples...
tuple<_Types...>,
typename __make_tuple_types<__remove_cvref_t<_Tuple0> >::type
>::type,
- __tuple_like<__libcpp_remove_reference_t<_Tuple1> >::value,
+ __tuple_like_ext<__libcpp_remove_reference_t<_Tuple1> >::value,
_Tuple1, _Tuples...>
{
};
@@ -1687,7 +1687,7 @@ template <class ..._Tuples> struct __tuple_cat_return;
template <class _Tuple0, class ..._Tuples>
struct __tuple_cat_return<_Tuple0, _Tuples...>
: public __tuple_cat_return_1<tuple<>,
- __tuple_like<__libcpp_remove_reference_t<_Tuple0> >::value, _Tuple0,
+ __tuple_like_ext<__libcpp_remove_reference_t<_Tuple0> >::value, _Tuple0,
_Tuples...>
{
};
diff --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp
index 97e945ef8fde8..f88ee37062d46 100644
--- a/libcxx/test/libcxx/private_headers.verify.cpp
+++ b/libcxx/test/libcxx/private_headers.verify.cpp
@@ -396,6 +396,7 @@ END-SCRIPT
#include <__fwd/span.h> // expected-error@*:* {{use of private header from outside its module: '__fwd/span.h'}}
#include <__fwd/string.h> // expected-error@*:* {{use of private header from outside its module: '__fwd/string.h'}}
#include <__fwd/string_view.h> // expected-error@*:* {{use of private header from outside its module: '__fwd/string_view.h'}}
+#include <__fwd/subrange.h> // expected-error@*:* {{use of private header from outside its module: '__fwd/subrange.h'}}
#include <__fwd/tuple.h> // expected-error@*:* {{use of private header from outside its module: '__fwd/tuple.h'}}
#include <__ios/fpos.h> // expected-error@*:* {{use of private header from outside its module: '__ios/fpos.h'}}
#include <__iterator/access.h> // expected-error@*:* {{use of private header from outside its module: '__iterator/access.h'}}
@@ -534,6 +535,7 @@ END-SCRIPT
#include <__ranges/data.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/data.h'}}
#include <__ranges/drop_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/drop_view.h'}}
#include <__ranges/drop_while_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/drop_while_view.h'}}
+#include <__ranges/elements_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/elements_view.h'}}
#include <__ranges/empty.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/empty.h'}}
#include <__ranges/empty_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/empty_view.h'}}
#include <__ranges/enable_borrowed_range.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/enable_borrowed_range.h'}}
@@ -567,10 +569,12 @@ END-SCRIPT
#include <__thread/timed_backoff_policy.h> // expected-error@*:* {{use of private header from outside its module: '__thread/timed_backoff_policy.h'}}
#include <__tuple_dir/apply_cv.h> // expected-error@*:* {{use of private header from outside its module: '__tuple_dir/apply_cv.h'}}
#include <__tuple_dir/make_tuple_types.h> // expected-error@*:* {{use of private header from outside its module: '__tuple_dir/make_tuple_types.h'}}
+#include <__tuple_dir/pair_like.h> // expected-error@*:* {{use of private header from outside its module: '__tuple_dir/pair_like.h'}}
#include <__tuple_dir/sfinae_helpers.h> // expected-error@*:* {{use of private header from outside its module: '__tuple_dir/sfinae_helpers.h'}}
#include <__tuple_dir/tuple_element.h> // expected-error@*:* {{use of private header from outside its module: '__tuple_dir/tuple_element.h'}}
#include <__tuple_dir/tuple_indices.h> // expected-error@*:* {{use of private header from outside its module: '__tuple_dir/tuple_indices.h'}}
#include <__tuple_dir/tuple_like.h> // expected-error@*:* {{use of private header from outside its module: '__tuple_dir/tuple_like.h'}}
+#include <__tuple_dir/tuple_like_ext.h> // expected-error@*:* {{use of private header from outside its module: '__tuple_dir/tuple_like_ext.h'}}
#include <__tuple_dir/tuple_size.h> // expected-error@*:* {{use of private header from outside its module: '__tuple_dir/tuple_size.h'}}
#include <__tuple_dir/tuple_types.h> // expected-error@*:* {{use of private header from outside its module: '__tuple_dir/tuple_types.h'}}
#include <__type_traits/add_const.h> // expected-error@*:* {{use of private header from outside its module: '__type_traits/add_const.h'}}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/elements_view.no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/elements_view.no_unique_address.compile.pass.cpp
new file mode 100644
index 0000000000000..6901745b6be29
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/elements_view.no_unique_address.compile.pass.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// clang-cl and cl currently don't support [[no_unique_address]]
+// XFAIL: msvc
+
+// Test the libc++ extension that the base view stored in `std::ranges::elements_view`
+// has been marked as _LIBCPP_NO_UNIQUE_ADDRESS
+
+#include <ranges>
+#include <tuple>
+
+
+struct EmptyView : std::ranges::view_base {
+ std::tuple<int>* begin() const;
+ std::tuple<int>* end() const;
+};
+
+using ElementsView = std::ranges::elements_view<EmptyView, 0>;
+
+struct TestClass {
+ [[no_unique_address]] ElementsView view;
+ int i;
+};
+
+static_assert(sizeof(TestClass) == sizeof(int));
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/sentinel.no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/sentinel.no_unique_address.compile.pass.cpp
new file mode 100644
index 0000000000000..2e69a327a7625
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/sentinel.no_unique_address.compile.pass.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// clang-cl and cl currently don't support [[no_unique_address]]
+// XFAIL: msvc
+
+// Test the libc++ extension that the sentinel stored in `std::ranges::elements_view::__sentinel`
+// has been marked as _LIBCPP_NO_UNIQUE_ADDRESS
+
+#include <ranges>
+#include <tuple>
+
+struct EmptySentinel {
+ friend bool operator==(std::tuple<int>* iter, EmptySentinel) { return iter; }
+};
+
+struct Range : std::ranges::view_base {
+ std::tuple<int>* begin() const;
+ EmptySentinel end() const;
+};
+
+using ElementsView = std::ranges::elements_view<Range, 0>;
+using ElementsSent = std::ranges::sentinel_t<ElementsView>;
+
+struct TestClass {
+ [[no_unique_address]] ElementsSent s;
+ int i;
+};
+
+static_assert(sizeof(TestClass) == sizeof(int));
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/adaptor.pass.cpp
new file mode 100644
index 0000000000000..d68d6e57e2ed5
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/adaptor.pass.cpp
@@ -0,0 +1,182 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// std::views::elements<N>
+// std::views::keys
+// std::views::values
+
+#include <algorithm>
+#include <cassert>
+#include <ranges>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+template <class T>
+struct View : std::ranges::view_base {
+ T* begin() const;
+ T* end() const;
+};
+
+static_assert(!std::is_invocable_v<decltype((std::views::elements<0>))>);
+static_assert(!std::is_invocable_v<decltype((std::views::elements<0>)), View<int>>);
+static_assert(std::is_invocable_v<decltype((std::views::elements<0>)), View<std::pair<int, int>>>);
+static_assert(std::is_invocable_v<decltype((std::views::elements<0>)), View<std::tuple<int>>>);
+static_assert(!std::is_invocable_v<decltype((std::views::elements<5>)), View<std::tuple<int>>>);
+
+static_assert(!std::is_invocable_v<decltype((std::views::keys))>);
+static_assert(!std::is_invocable_v<decltype((std::views::keys)), View<int>>);
+static_assert(std::is_invocable_v<decltype((std::views::keys)), View<std::pair<int, int>>>);
+static_assert(std::is_invocable_v<decltype((std::views::keys)), View<std::tuple<int>>>);
+
+static_assert(!std::is_invocable_v<decltype((std::views::values))>);
+static_assert(!std::is_invocable_v<decltype((std::views::values)), View<int>>);
+static_assert(std::is_invocable_v<decltype((std::views::values)), View<std::pair<int, int>>>);
+static_assert(!std::is_invocable_v<decltype((std::views::values)), View<std::tuple<int>>>);
+
+template <class View, class T>
+concept CanBePiped =
+ requires(View&& view, T&& t) {
+ { std::forward<View>(view) | std::forward<T>(t) };
+ };
+
+static_assert(!CanBePiped<View<int>, decltype((std::views::elements<0>))>);
+static_assert(CanBePiped<View<std::pair<int, int>>, decltype((std::views::elements<0>))>);
+static_assert(CanBePiped<View<std::tuple<int>>, decltype((std::views::elements<0>))>);
+static_assert(!CanBePiped<View<std::tuple<int>>, decltype((std::views::elements<5>))>);
+
+static_assert(!CanBePiped<View<int>, decltype((std::views::keys))>);
+static_assert(CanBePiped<View<std::pair<int, int>>, decltype((std::views::keys))>);
+static_assert(CanBePiped<View<std::tuple<int>>, decltype((std::views::keys))>);
+
+static_assert(!CanBePiped<View<int>, decltype((std::views::values))>);
+static_assert(CanBePiped<View<std::pair<int, int>>, decltype((std::views::values))>);
+static_assert(!CanBePiped<View<std::tuple<int>>, decltype((std::views::values))>);
+
+constexpr bool test() {
+ std::pair<int, int> buff[] = {{1, 2}, {3, 4}, {5, 6}};
+
+ // Test `views::elements<N>(v)`
+ {
+ using Result = std::ranges::elements_view<std::ranges::ref_view<std::pair<int, int>[3]>, 0>;
+ std::same_as<Result> decltype(auto) result = std::views::elements<0>(buff);
+ auto expected = {1, 3, 5};
+ assert(std::ranges::equal(result, expected));
+ }
+
+ // Test `views::keys(v)`
+ {
+ using Result = std::ranges::elements_view<std::ranges::ref_view<std::pair<int, int>[3]>, 0>;
+ std::same_as<Result> decltype(auto) result = std::views::keys(buff);
+ auto expected = {1, 3, 5};
+ assert(std::ranges::equal(result, expected));
+ }
+
+ // Test `views::values(v)`
+ {
+ using Result = std::ranges::elements_view<std::ranges::ref_view<std::pair<int, int>[3]>, 1>;
+ std::same_as<Result> decltype(auto) result = std::views::values(buff);
+ auto expected = {2, 4, 6};
+ assert(std::ranges::equal(result, expected));
+ }
+
+ // Test `v | views::elements<N>`
+ {
+ using Result = std::ranges::elements_view<std::ranges::ref_view<std::pair<int, int>[3]>, 1>;
+ std::same_as<Result> decltype(auto) result = buff | std::views::elements<1>;
+ auto expected = {2, 4, 6};
+ assert(std::ranges::equal(result, expected));
+ }
+
+ // Test `v | views::keys`
+ {
+ using Result = std::ranges::elements_view<std::ranges::ref_view<std::pair<int, int>[3]>, 0>;
+ std::same_as<Result> decltype(auto) result = buff | std::views::keys;
+ auto expected = {1, 3, 5};
+ assert(std::ranges::equal(result, expected));
+ }
+
+ // Test `v | views::values`
+ {
+ using Result = std::ranges::elements_view<std::ranges::ref_view<std::pair<int, int>[3]>, 1>;
+ std::same_as<Result> decltype(auto) result = buff | std::views::values;
+ auto expected = {2, 4, 6};
+ assert(std::ranges::equal(result, expected));
+ }
+
+ // Test views::elements<0> | views::elements<0>
+ {
+ std::pair<std::tuple<int>, std::tuple<int>> nested[] = {{{1}, {2}}, {{3}, {4}}, {{5}, {6}}};
+ using Result = std::ranges::elements_view<
+ std::ranges::elements_view<std::ranges::ref_view<std::pair<std::tuple<int>, std::tuple<int>>[3]>, 0>,
+ 0>;
+ auto const partial = std::views::elements<0> | std::views::elements<0>;
+ std::same_as<Result> decltype(auto) result = nested | partial;
+ auto expected = {1, 3, 5};
+ assert(std::ranges::equal(result, expected));
+ }
+
+ // Test views::keys | views::keys
+ {
+ std::pair<std::tuple<int>, std::tuple<int>> nested[] = {{{1}, {2}}, {{3}, {4}}, {{5}, {6}}};
+ using Result = std::ranges::elements_view<
+ std::ranges::elements_view<std::ranges::ref_view<std::pair<std::tuple<int>, std::tuple<int>>[3]>, 0>,
+ 0>;
+ auto const partial = std::views::keys | std::views::keys;
+ std::same_as<Result> decltype(auto) result = nested | partial;
+ auto expected = {1, 3, 5};
+ assert(std::ranges::equal(result, expected));
+ }
+
+ // Test views::values | views::values
+ {
+ std::pair<std::tuple<int>, std::tuple<int, int>> nested[] = {{{1}, {2, 3}}, {{4}, {5, 6}}, {{7}, {8, 9}}};
+ using Result = std::ranges::elements_view<
+ std::ranges::elements_view<std::ranges::ref_view<std::pair<std::tuple<int>, std::tuple<int, int>>[3]>, 1>,
+ 1>;
+ auto const partial = std::views::values | std::views::values;
+ std::same_as<Result> decltype(auto) result = nested | partial;
+ auto expected = {3, 6, 9};
+ assert(std::ranges::equal(result, expected));
+ }
+
+ // Test views::keys | views::values
+ {
+ std::pair<std::tuple<int, int>, std::tuple<int>> nested[] = {{{1, 2}, {3}}, {{4, 5}, {6}}, {{7, 8}, {9}}};
+ using Result = std::ranges::elements_view<
+ std::ranges::elements_view<std::ranges::ref_view<std::pair<std::tuple<int, int>, std::tuple<int>>[3]>, 0>,
+ 1>;
+ auto const partial = std::views::keys | std::views::values;
+ std::same_as<Result> decltype(auto) result = nested | partial;
+ auto expected = {2, 5, 8};
+ assert(std::ranges::equal(result, expected));
+ }
+
+ // Test views::values | views::keys
+ {
+ std::pair<std::tuple<int>, std::tuple<int, int>> nested[] = {{{1}, {2, 3}}, {{4}, {5, 6}}, {{7}, {8, 9}}};
+ using Result = std::ranges::elements_view<
+ std::ranges::elements_view<std::ranges::ref_view<std::pair<std::tuple<int>, std::tuple<int, int>>[3]>, 1>,
+ 0>;
+ auto const partial = std::views::values | std::views::keys;
+ std::same_as<Result> decltype(auto) result = nested | partial;
+ auto expected = {2, 5, 8};
+ assert(std::ranges::equal(result, expected));
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/base.pass.cpp
new file mode 100644
index 0000000000000..7fbd27d58190e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/base.pass.cpp
@@ -0,0 +1,84 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr V base() const & requires copy_constructible<V> { return base_; }
+// constexpr V base() && { return std::move(base_); }
+
+#include <cassert>
+#include <ranges>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+#include "MoveOnly.h"
+
+struct View : std::ranges::view_base {
+ int i;
+ std::tuple<int>* begin() const;
+ std::tuple<int>* end() const;
+};
+
+struct MoveOnlyView : View {
+ MoveOnly mo;
+};
+
+template <class T>
+concept HasBase = requires(T&& t) { std::forward<T>(t).base(); };
+
+static_assert(HasBase<std::ranges::elements_view<View, 0> const&>);
+static_assert(HasBase<std::ranges::elements_view<View, 0>&&>);
+
+static_assert(!HasBase<std::ranges::elements_view<MoveOnlyView, 0> const&>);
+static_assert(HasBase<std::ranges::elements_view<MoveOnlyView, 0>&&>);
+
+constexpr bool test() {
+ // const &
+ {
+ const std::ranges::elements_view<View, 0> ev{View{{}, 5}};
+ std::same_as<View> decltype(auto) v = ev.base();
+ assert(v.i == 5);
+ }
+
+ // &
+ {
+ std::ranges::elements_view<View, 0> ev{View{{}, 5}};
+ std::same_as<View> decltype(auto) v = ev.base();
+ assert(v.i == 5);
+ }
+
+ // &&
+ {
+ std::ranges::elements_view<View, 0> ev{View{{}, 5}};
+ std::same_as<View> decltype(auto) v = std::move(ev).base();
+ assert(v.i == 5);
+ }
+
+ // const &&
+ {
+ const std::ranges::elements_view<View, 0> ev{View{{}, 5}};
+ std::same_as<View> decltype(auto) v = std::move(ev).base();
+ assert(v.i == 5);
+ }
+
+ // move only
+ {
+ std::ranges::elements_view<MoveOnlyView, 0> ev{MoveOnlyView{{}, 5}};
+ std::same_as<MoveOnlyView> decltype(auto) v = std::move(ev).base();
+ assert(v.mo.get() == 5);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/begin.pass.cpp
new file mode 100644
index 0000000000000..824e8f8ed4873
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/begin.pass.cpp
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto begin() requires (!simple-view<V>)
+// constexpr auto begin() const requires range<const V>
+
+#include <cassert>
+#include <concepts>
+#include <ranges>
+#include <tuple>
+#include <utility>
+
+#include "types.h"
+
+template <class T>
+concept HasConstBegin = requires(const T ct) { ct.begin(); };
+
+template <class T>
+concept HasBegin = requires(T t) { t.begin(); };
+
+template <class T>
+concept HasConstAndNonConstBegin =
+ HasConstBegin<T> &&
+ // because const begin and non-const begin returns
diff erent types (iterator<true>, iterator<false>)
+ requires(T t, const T ct) { requires !std::same_as<decltype(t.begin()), decltype(ct.begin())>; };
+
+template <class T>
+concept HasOnlyNonConstBegin = HasBegin<T> && !HasConstBegin<T>;
+
+template <class T>
+concept HasOnlyConstBegin = HasConstBegin<T> && !HasConstAndNonConstBegin<T>;
+
+struct NoConstBeginView : TupleBufferView {
+ using TupleBufferView::TupleBufferView;
+ constexpr std::tuple<int>* begin() { return buffer_; }
+ constexpr std::tuple<int>* end() { return buffer_ + size_; }
+};
+
+// simple-view<V>
+static_assert(HasOnlyConstBegin<std::ranges::elements_view<SimpleCommon, 0>>);
+
+// !simple-view<V> && range<const V>
+static_assert(HasConstAndNonConstBegin<std::ranges::elements_view<NonSimpleCommon, 0>>);
+
+// !range<const V>
+static_assert(HasOnlyNonConstBegin<std::ranges::elements_view<NoConstBeginView, 0>>);
+
+constexpr bool test() {
+ std::tuple<int> buffer[] = {{1}, {2}};
+ {
+ // underlying iterator should be pointing to the first element
+ auto ev = std::views::elements<0>(buffer);
+ auto iter = ev.begin();
+ assert(&(*iter) == &std::get<0>(buffer[0]));
+ }
+
+ {
+ // underlying range models simple-view
+ auto v = std::views::elements<0>(SimpleCommon{buffer});
+ static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+ assert(v.begin() == std::as_const(v).begin());
+ auto&& r = *std::as_const(v).begin();
+ assert(&r == &std::get<0>(buffer[0]));
+ }
+
+ {
+ // underlying const R is not a range
+ auto v = std::views::elements<0>(NoConstBeginView{buffer});
+ auto&& r = *v.begin();
+ assert(&r == &std::get<0>(buffer[0]));
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/borrowed.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/borrowed.compile.pass.cpp
new file mode 100644
index 0000000000000..8cc08e355d4f9
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/borrowed.compile.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+// template<class T, size_t N>
+// inline constexpr bool enable_borrowed_range<elements_view<T, N>> =
+// enable_borrowed_range<T>;
+
+#include <ranges>
+#include <tuple>
+
+struct NonBorrowed : std::ranges::view_base {
+ std::tuple<int>* begin();
+ std::tuple<int>* end();
+};
+
+struct Borrowed : std::ranges::view_base {
+ std::tuple<int>* begin();
+ std::tuple<int>* end();
+};
+
+template <>
+inline constexpr bool std::ranges::enable_borrowed_range<Borrowed> = true;
+
+static_assert(!std::ranges::borrowed_range<std::ranges::elements_view<NonBorrowed, 0>>);
+static_assert(std::ranges::borrowed_range<std::ranges::elements_view<Borrowed, 0>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/ctor.default.pass.cpp
new file mode 100644
index 0000000000000..44a566d765ee4
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/ctor.default.pass.cpp
@@ -0,0 +1,47 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// elements_view() requires default_initializable<V> = default;
+
+#include <cassert>
+#include <ranges>
+#include <tuple>
+#include <type_traits>
+
+template <bool DefaultInitializable>
+struct View : std::ranges::view_base {
+ int i = 42;
+ constexpr explicit View()
+ requires DefaultInitializable
+ = default;
+ std::tuple<int>* begin() const;
+ std::tuple<int>* end() const;
+};
+
+
+// clang-format off
+static_assert( std::is_default_constructible_v<std::ranges::elements_view<View<true >, 0>>);
+static_assert(!std::is_default_constructible_v<std::ranges::elements_view<View<false>, 0>>);
+// clang-format on
+
+constexpr bool test() {
+ {
+ std::ranges::elements_view<View<true>, 0> ev = {};
+ assert(ev.base().i == 42);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/ctor.view.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/ctor.view.pass.cpp
new file mode 100644
index 0000000000000..eda9266b1289c
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/ctor.view.pass.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr explicit elements_view(V base);
+
+#include <cassert>
+#include <ranges>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+#include "MoveOnly.h"
+
+struct View : std::ranges::view_base {
+ MoveOnly mo;
+ std::tuple<int>* begin() const;
+ std::tuple<int>* end() const;
+};
+
+// Test Explicit
+static_assert(std::is_constructible_v<std::ranges::elements_view<View, 0>, View>);
+static_assert(!std::is_convertible_v<View, std::ranges::elements_view<View, 0>>);
+
+constexpr bool test() {
+ {
+ std::ranges::elements_view<View, 0> ev{View{{}, MoveOnly{5}}};
+ assert(std::move(ev).base().mo.get() == 5);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/end.pass.cpp
new file mode 100644
index 0000000000000..003ce50381f77
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/end.pass.cpp
@@ -0,0 +1,142 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto end() requires (!simple-view<V> && !common_range<V>)
+// constexpr auto end() requires (!simple-view<V> && common_range<V>)
+// constexpr auto end() const requires range<const V>
+// constexpr auto end() const requires common_range<const V>
+
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <type_traits>
+#include <utility>
+
+#include "types.h"
+
+// | simple | common | v.end() | as_const(v)
+// | | | | .end()
+// |--------|--------|------------------|---------------
+// | Y | Y | iterator<true> | iterator<true>
+// | Y | N | sentinel<true> | sentinel<true>
+// | N | Y | iterator<false> | iterator<true>
+// | N | N | sentinel<false> | sentinel<true>
+
+// !range<const V>
+template <class T>
+concept HasEnd = requires(T t) { t.end(); };
+
+template <class T>
+concept HasConstEnd = requires(const T ct) { ct.end(); };
+
+struct NoConstEndView : TupleBufferView {
+ using TupleBufferView::TupleBufferView;
+ constexpr std::tuple<int>* begin() { return buffer_; }
+ constexpr std::tuple<int>* end() { return buffer_ + size_; }
+};
+
+static_assert(HasEnd<std::ranges::elements_view<NoConstEndView, 0>>);
+static_assert(!HasConstEnd<std::ranges::elements_view<NoConstEndView, 0>>);
+
+constexpr bool test() {
+ std::tuple<int> buffer[] = {{1}, {2}, {3}};
+
+ // simple-view && common_view
+ {
+ SimpleCommon v{buffer};
+ auto ev = std::views::elements<0>(v);
+
+ auto it = ev.begin();
+ decltype(auto) st = ev.end();
+ assert(st == it + 3);
+
+ auto const_it = std::as_const(ev).begin();
+ decltype(auto) const_st = std::as_const(ev).end();
+ assert(const_st == const_it + 3);
+
+ // Both iterator<true>
+ static_assert(std::same_as<decltype(st), decltype(const_st)>);
+ static_assert(std::same_as<decltype(st), decltype(it)>);
+ static_assert(std::same_as<decltype(const_st), decltype(const_it)>);
+ }
+
+ // simple-view && !common_view
+ {
+ SimpleNonCommon v{buffer};
+ auto ev = std::views::elements<0>(v);
+
+ auto it = ev.begin();
+ decltype(auto) st = ev.end();
+ assert(st == it + 3);
+
+ auto const_it = std::as_const(ev).begin();
+ decltype(auto) const_st = std::as_const(ev).end();
+ assert(const_st == const_it + 3);
+
+ // Both iterator<true>
+ static_assert(std::same_as<decltype(st), decltype(const_st)>);
+ static_assert(!std::same_as<decltype(st), decltype(it)>);
+ static_assert(!std::same_as<decltype(const_st), decltype(const_it)>);
+ }
+
+ // !simple-view && common_view
+ {
+ NonSimpleCommon v{buffer};
+ auto ev = std::views::elements<0>(v);
+
+ auto it = ev.begin();
+ decltype(auto) st = ev.end();
+ assert(st == it + 3);
+
+ auto const_it = std::as_const(ev).begin();
+ decltype(auto) const_st = std::as_const(ev).end();
+ assert(const_st == const_it + 3);
+
+ // iterator<false> and iterator<true>
+ static_assert(!std::same_as<decltype(st), decltype(const_st)>);
+ static_assert(std::same_as<decltype(st), decltype(it)>);
+ static_assert(std::same_as<decltype(const_st), decltype(const_it)>);
+ }
+
+ // !simple-view && !common_view
+ {
+ NonSimpleNonCommon v{buffer};
+ auto ev = std::views::elements<0>(v);
+
+ auto it = ev.begin();
+ decltype(auto) st = ev.end();
+ assert(st == it + 3);
+
+ auto const_it = std::as_const(ev).begin();
+ decltype(auto) const_st = std::as_const(ev).end();
+ assert(const_st == const_it + 3);
+
+ // sentinel<false> and sentinel<true>
+ static_assert(!std::same_as<decltype(st), decltype(const_st)>);
+ static_assert(!std::same_as<decltype(st), decltype(it)>);
+ static_assert(!std::same_as<decltype(const_st), decltype(const_it)>);
+ }
+
+ // LWG 3406 elements_view::begin() and elements_view::end() have incompatible constraints
+ {
+ std::tuple<int, int> x[] = {{0, 0}};
+ std::ranges::subrange r = {std::counted_iterator(x, 1), std::default_sentinel};
+ auto v = r | std::views::elements<0>;
+ assert(v.begin() != v.end());
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/general.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/general.pass.cpp
new file mode 100644
index 0000000000000..78792ae54bdbf
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/general.pass.cpp
@@ -0,0 +1,103 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Some basic examples of how elements_view might be used in the wild. This is a general
+// collection of sample algorithms and functions that try to mock general usage of
+// this view.
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <map>
+#include <ranges>
+#include <string_view>
+#include <tuple>
+#include <utility>
+
+#include "test_iterators.h"
+
+int main(int, char**) {
+ using namespace std::string_view_literals;
+ auto historicalFigures =
+ std::map{std::pair{"Lovelace"sv, 1815}, {"Turing"sv, 1912}, {"Babbage"sv, 1791}, {"Hamilton"sv, 1936}};
+ auto expectedYears = {1791, 1936, 1815, 1912};
+
+ // views::elements<N>
+ {
+ auto names = historicalFigures | std::views::elements<0>;
+ auto expectedNames = {"Babbage"sv, "Hamilton"sv, "Lovelace"sv, "Turing"sv};
+ assert(std::ranges::equal(names, expectedNames));
+
+ auto birth_years = historicalFigures | std::views::elements<1>;
+ assert(std::ranges::equal(birth_years, expectedYears));
+ }
+
+ // views::keys
+ {
+ auto names = historicalFigures | std::views::keys;
+ auto expectedNames = {"Babbage"sv, "Hamilton"sv, "Lovelace"sv, "Turing"sv};
+ assert(std::ranges::equal(names, expectedNames));
+ }
+
+ // views::values
+ {
+ auto is_even = [](const auto x) { return x % 2 == 0; };
+ assert(std::ranges::count_if(historicalFigures | std::views::values, is_even) == 2);
+ }
+
+ // array
+ {
+ std::array<int, 3> arrs[] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
+ auto ev = arrs | std::views::elements<2>;
+ auto expected = {3, 6, 9};
+ assert(std::ranges::equal(ev, expected));
+ }
+
+ // pair
+ {
+ std::pair<double, int> ps[] = {{1.0, 2}, {3.0, 4}, {5.0, 6}};
+ auto ev = ps | std::views::elements<1>;
+ auto expected = {2, 4, 6};
+ assert(std::ranges::equal(ev, expected));
+ }
+
+ // tuple
+ {
+ std::tuple<short> tps[] = {{1}, {2}, {3}};
+ auto ev = tps | std::views::elements<0>;
+ auto expected = {1, 2, 3};
+ assert(std::ranges::equal(ev, expected));
+ }
+
+ // subrange
+ {
+ int is[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+ using Iter = forward_iterator<int*>;
+ using Sent = sentinel_wrapper<Iter>;
+ using SubRange = std::ranges::subrange<Iter, Sent>;
+ SubRange sr[] = {
+ {Iter{is}, Sent{Iter{is + 1}}},
+ {Iter{is + 2}, Sent{Iter{is + 5}}},
+ {Iter{is + 6}, Sent{Iter{is + 8}}},
+ };
+
+ auto iters = sr | std::views::elements<0>;
+ static_assert(std::is_same_v<Iter, std::ranges::range_reference_t<decltype(iters)>>);
+ auto expectedIters = {is, is + 2, is + 6};
+ assert(std::ranges::equal(iters | std::views::transform([](auto&& iter) { return base(iter); }), expectedIters));
+
+ auto sentinels = sr | std::views::elements<1>;
+ static_assert(std::is_same_v<Sent, std::ranges::range_reference_t<decltype(sentinels)>>);
+ auto expectedSentinels = {is + 1, is + 5, is + 8};
+ assert(std::ranges::equal(
+ sentinels | std::views::transform([](auto&& st) { return base(base(st)); }), expectedSentinels));
+ }
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/arithmetic.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/arithmetic.pass.cpp
new file mode 100644
index 0000000000000..7a4ce83f1a9f6
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/arithmetic.pass.cpp
@@ -0,0 +1,123 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr iterator& operator+=(
diff erence_type n)
+// requires random_access_range<Base>;
+//
+// constexpr iterator& operator-=(
diff erence_type n)
+// requires random_access_range<Base>;
+//
+// friend constexpr iterator operator+(const iterator& x,
diff erence_type y)
+// requires random_access_range<Base>;
+//
+// friend constexpr iterator operator+(
diff erence_type x, const iterator& y)
+// requires random_access_range<Base>;
+//
+// friend constexpr iterator operator-(const iterator& x,
diff erence_type y)
+// requires random_access_range<Base>;
+//
+// friend constexpr
diff erence_type operator-(const iterator& x, const iterator& y)
+// requires sized_sentinel_for<iterator_t<Base>, iterator_t<Base>>;
+
+#include <ranges>
+
+#include <tuple>
+
+#include "test_iterators.h"
+
+template <class T, class U>
+concept CanPlus = requires(T t, U u) { t + u; };
+
+template <class T, class U>
+concept CanPlusEqual = requires(T t, U u) { t += u; };
+
+template <class T, class U>
+concept CanMinus = requires(T t, U u) { t - u; };
+
+template <class T, class U>
+concept CanMinusEqual = requires(T t, U u) { t -= u; };
+
+template <class BaseRange>
+using ElemIter = std::ranges::iterator_t<std::ranges::elements_view<BaseRange, 0>>;
+
+using RandomAccessRange = std::ranges::subrange<std::tuple<int>*>;
+static_assert(std::ranges::random_access_range<RandomAccessRange>);
+static_assert(std::sized_sentinel_for<std::ranges::iterator_t<RandomAccessRange>, //
+ std::ranges::iterator_t<RandomAccessRange>>);
+
+static_assert(CanPlus<ElemIter<RandomAccessRange>, int>);
+static_assert(CanPlus<int, ElemIter<RandomAccessRange>>);
+static_assert(CanPlusEqual<ElemIter<RandomAccessRange>, int>);
+static_assert(CanMinus<ElemIter<RandomAccessRange>, int>);
+static_assert(CanMinus<ElemIter<RandomAccessRange>, ElemIter<RandomAccessRange>>);
+static_assert(CanMinusEqual<ElemIter<RandomAccessRange>, int>);
+
+using BidiRange = std::ranges::subrange<bidirectional_iterator<std::tuple<int>*>>;
+static_assert(!std::ranges::random_access_range<BidiRange>);
+static_assert(!std::sized_sentinel_for<std::ranges::iterator_t<BidiRange>, //
+ std::ranges::iterator_t<BidiRange>>);
+
+static_assert(!CanPlus<ElemIter<BidiRange>, int>);
+static_assert(!CanPlus<int, ElemIter<BidiRange>>);
+static_assert(!CanPlusEqual<ElemIter<BidiRange>, int>);
+static_assert(!CanMinus<ElemIter<BidiRange>, int>);
+static_assert(!CanMinus<ElemIter<BidiRange>, ElemIter<BidiRange>>);
+static_assert(!CanMinusEqual<ElemIter<BidiRange>, int>);
+
+constexpr bool test() {
+ std::tuple<int> ts[] = {{1}, {2}, {3}, {4}};
+
+ RandomAccessRange r{&ts[0], &ts[0] + 4};
+ auto ev = r | std::views::elements<0>;
+ {
+ // operator+(x, n) operator+(n,x) and operator+=
+ auto it1 = ev.begin();
+
+ auto it2 = it1 + 3;
+ assert(it2.base() == &ts[3]);
+
+ auto it3 = 3 + it1;
+ assert(it3.base() == &ts[3]);
+
+ it1 += 3;
+ assert(it1 == it2);
+ assert(it1.base() == &ts[3]);
+ }
+
+ {
+ // operator-(x, n) and operator-=
+ auto it1 = ev.end();
+
+ auto it2 = it1 - 3;
+ assert(it2.base() == &ts[1]);
+
+ it1 -= 3;
+ assert(it1 == it2);
+ assert(it1.base() == &ts[1]);
+ }
+
+ {
+ // operator-(x, y)
+ assert((ev.end() - ev.begin()) == 4);
+
+ auto it1 = ev.begin() + 2;
+ auto it2 = ev.end() - 1;
+ assert((it1 - it2) == -1);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
new file mode 100644
index 0000000000000..3729c8e543113
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp
@@ -0,0 +1,98 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr const iterator_t<Base>& base() const & noexcept;
+// constexpr iterator_t<Base> base() &&;
+
+#include <cassert>
+#include <ranges>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+#include "MoveOnly.h"
+#include "../types.h"
+
+// Test Noexcept
+template <class T>
+concept IsBaseNoexcept =
+ requires {
+ { std::declval<T>().base() } noexcept;
+ };
+
+using BaseView = std::ranges::subrange<std::tuple<int>*>;
+using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<BaseView, 0>>;
+
+static_assert(IsBaseNoexcept<const ElementsIter&>);
+static_assert(IsBaseNoexcept<ElementsIter&>);
+static_assert(IsBaseNoexcept<const ElementsIter&&>);
+static_assert(!IsBaseNoexcept<ElementsIter&&>);
+
+constexpr bool test() {
+ std::tuple<int> t{5};
+
+ // const &
+ {
+ const ElementsIter it{&t};
+ decltype(auto) base = it.base();
+ static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
+ assert(base == &t);
+ }
+
+ // &
+ {
+ ElementsIter it{&t};
+ decltype(auto) base = it.base();
+ static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
+ assert(base == &t);
+ }
+
+ // &&
+ {
+ ElementsIter it{&t};
+ decltype(auto) base = std::move(it).base();
+ static_assert(std::is_same_v<decltype(base), std::tuple<int>*>);
+ assert(base == &t);
+ }
+
+ // const &&
+ {
+ const ElementsIter it{&t};
+ decltype(auto) base = std::move(it).base();
+ static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>);
+ assert(base == &t);
+ }
+
+ // move only
+ {
+ struct MoveOnlyIter : IterBase<MoveOnlyIter> {
+ MoveOnly mo;
+ };
+ struct Sent {
+ constexpr bool operator==(const MoveOnlyIter&) const { return true; }
+ };
+
+ using MoveOnlyElemIter =
+ std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>;
+
+ MoveOnlyElemIter it{MoveOnlyIter{{}, MoveOnly{5}}};
+ decltype(auto) base = std::move(it).base();
+ static_assert(std::is_same_v<decltype(base), MoveOnlyIter>);
+ assert(base.mo.get() == 5);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/compare.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/compare.pass.cpp
new file mode 100644
index 0000000000000..16df3e40bd77a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/compare.pass.cpp
@@ -0,0 +1,155 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// friend constexpr bool operator==(const iterator& x, const iterator& y)
+// requires equality_comparable<iterator_t<Base>>;
+// friend constexpr bool operator<(const iterator& x, const iterator& y)
+// requires random_access_range<Base>;
+// friend constexpr bool operator>(const iterator& x, const iterator& y)
+// requires random_access_range<Base>;
+// friend constexpr bool operator<=(const iterator& x, const iterator& y)
+// requires random_access_range<Base>;
+// friend constexpr bool operator>=(const iterator& x, const iterator& y)
+// requires random_access_range<Base>;
+// friend constexpr auto operator<=>(const iterator& x, const iterator& y)
+// requires random_access_range<Base> && three_way_comparable<iterator_t<Base>>;
+
+#include <compare>
+#include <functional>
+#include <ranges>
+#include <tuple>
+
+#include "test_iterators.h"
+
+constexpr void compareOperatorTest(const auto& iter1, const auto& iter2) {
+ assert(!(iter1 < iter1));
+ assert(iter1 < iter2);
+ assert(!(iter2 < iter1));
+
+ assert(iter1 <= iter1);
+ assert(iter1 <= iter2);
+ assert(!(iter2 <= iter1));
+
+ assert(!(iter1 > iter1));
+ assert(!(iter1 > iter2));
+ assert(iter2 > iter1);
+
+ assert(iter1 >= iter1);
+ assert(!(iter1 >= iter2));
+ assert(iter2 >= iter1);
+
+ assert(iter1 == iter1);
+ assert(!(iter1 == iter2));
+ assert(iter2 == iter2);
+
+ assert(!(iter1 != iter1));
+ assert(iter1 != iter2);
+ assert(!(iter2 != iter2));
+}
+
+constexpr void inequalityOperatorsDoNotExistTest(const auto& iter1, const auto& iter2) {
+ using Iter1 = decltype(iter1);
+ using Iter2 = decltype(iter2);
+ static_assert(!std::is_invocable_v<std::less<>, Iter1, Iter2>);
+ static_assert(!std::is_invocable_v<std::less_equal<>, Iter1, Iter2>);
+ static_assert(!std::is_invocable_v<std::greater<>, Iter1, Iter2>);
+ static_assert(!std::is_invocable_v<std::greater_equal<>, Iter1, Iter2>);
+}
+
+constexpr bool test() {
+ std::tuple<int> ts[] = {{1}, {2}, {3}};
+
+ {
+ // Test a new-school iterator with operator<=>; the iterator should also have operator<=>.
+ using It = three_way_contiguous_iterator<std::tuple<int>*>;
+ using Subrange = std::ranges::subrange<It>;
+ static_assert(std::three_way_comparable<It>);
+ using R = std::ranges::elements_view<Subrange, 0>;
+ static_assert(std::three_way_comparable<std::ranges::iterator_t<R>>);
+
+ auto ev = Subrange{It{&ts[0]}, It{&ts[0] + 3}} | std::views::elements<0>;
+ auto iter1 = ev.begin();
+ auto iter2 = iter1 + 1;
+
+ compareOperatorTest(iter1, iter2);
+
+ assert((iter1 <=> iter2) == std::strong_ordering::less);
+ assert((iter1 <=> iter1) == std::strong_ordering::equal);
+ assert((iter2 <=> iter1) == std::strong_ordering::greater);
+ }
+
+ {
+ // Test an old-school iterator with no operator<=>; the elements view iterator shouldn't have
+ // operator<=> either.
+ using It = random_access_iterator<std::tuple<int>*>;
+ using Subrange = std::ranges::subrange<It>;
+ static_assert(!std::three_way_comparable<It>);
+ using R = std::ranges::elements_view<Subrange, 0>;
+ static_assert(!std::three_way_comparable<std::ranges::iterator_t<R>>);
+
+ auto ev = Subrange{It{&ts[0]}, It{&ts[0] + 3}} | std::views::elements<0>;
+ auto iter1 = ev.begin();
+ auto iter2 = iter1 + 1;
+
+ compareOperatorTest(iter1, iter2);
+ }
+
+ {
+ // non random_access_range
+ using It = bidirectional_iterator<std::tuple<int>*>;
+ using Subrange = std::ranges::subrange<It>;
+ static_assert(!std::ranges::random_access_range<Subrange>);
+ using R = std::ranges::elements_view<Subrange, 0>;
+ static_assert(!std::three_way_comparable<std::ranges::iterator_t<R>>);
+
+ auto ev = Subrange{It{&ts[0]}, It{&ts[0] + 1}} | std::views::elements<0>;
+
+ auto it1 = ev.begin();
+ auto it2 = ev.end();
+
+ assert(it1 == it1);
+ assert(!(it1 != it1));
+ assert(it2 == it2);
+ assert(!(it2 != it2));
+
+ assert(it1 != it2);
+
+ ++it1;
+ assert(it1 == it2);
+
+ inequalityOperatorsDoNotExistTest(it1, it2);
+ }
+
+ {
+ // underlying iterator does not support ==
+ using Iter = cpp20_input_iterator<std::tuple<int>*>;
+ using Sent = sentinel_wrapper<Iter>;
+ using Subrange = std::ranges::subrange<Iter, Sent>;
+ using R = std::ranges::elements_view<Subrange, 0>;
+ static_assert(!std::three_way_comparable<std::ranges::iterator_t<R>>);
+
+ auto ev = Subrange{Iter{&ts[0]}, Sent{Iter{&ts[0] + 1}}} | std::views::elements<0>;
+ auto it = ev.begin();
+
+ using ElemIter = decltype(it);
+ static_assert(!std::invocable<std::equal_to<>, ElemIter, ElemIter>);
+ static_assert(!std::invocable<std::not_equal_to<>, ElemIter, ElemIter>);
+ inequalityOperatorsDoNotExistTest(it, it);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
new file mode 100644
index 0000000000000..5a280d294aecc
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp
@@ -0,0 +1,54 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr explicit iterator(iterator_t<Base> current);
+
+#include <cassert>
+#include <ranges>
+#include <tuple>
+
+#include "../types.h"
+
+// Test explicit
+using BaseIter = std::tuple<int>*;
+using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<BaseIter, BaseIter>, 0>>;
+
+static_assert(std::is_constructible_v<ElementsIter, BaseIter>);
+static_assert(!std::is_convertible_v<BaseIter, ElementsIter>);
+
+struct TracedMoveIter : IterBase<TracedMoveIter>{
+ bool moved = false;
+
+ constexpr TracedMoveIter() = default;
+ constexpr TracedMoveIter(const TracedMoveIter&) = default;
+ constexpr TracedMoveIter(TracedMoveIter&&) : moved{true} {}
+ constexpr TracedMoveIter& operator=(TracedMoveIter&&) = default;
+ constexpr TracedMoveIter& operator=(const TracedMoveIter&) = default;
+};
+
+struct TracedMoveView : std::ranges::view_base {
+ TracedMoveIter begin() const;
+ TracedMoveIter end() const;
+};
+
+constexpr bool test() {
+ using Iter = std::ranges::iterator_t<std::ranges::elements_view<TracedMoveView, 0>>;
+ Iter iter{TracedMoveIter{}};
+ assert(iter.base().moved);
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.default.pass.cpp
new file mode 100644
index 0000000000000..62aa9b2a1a1f7
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.default.pass.cpp
@@ -0,0 +1,58 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// iterator() requires default_initializable<iterator_t<Base>> = default;
+
+#include <ranges>
+#include <tuple>
+
+#include "../types.h"
+
+struct PODIter : IterBase<PODIter> {
+ int i; // deliberately uninitialised
+};
+
+struct IterDefaultCtrView : std::ranges::view_base {
+ PODIter begin() const;
+ PODIter end() const;
+};
+
+struct IterNoDefaultCtrView : std::ranges::view_base {
+ cpp20_input_iterator<std::tuple<int>*> begin() const;
+ sentinel_wrapper<cpp20_input_iterator<std::tuple<int>*>> end() const;
+};
+
+template <class View, size_t N>
+using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<View, N>>;
+
+static_assert(!std::default_initializable<ElementsIter<IterNoDefaultCtrView, 0>>);
+static_assert(std::default_initializable<ElementsIter<IterDefaultCtrView, 0>>);
+
+constexpr bool test() {
+ using Iter = ElementsIter<IterDefaultCtrView, 0>;
+ {
+ Iter iter;
+ assert(iter.base().i == 0); // PODIter has to be initialised to have value 0
+ }
+
+ {
+ Iter iter = {};
+ assert(iter.base().i == 0); // PODIter has to be initialised to have value 0
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
new file mode 100644
index 0000000000000..6756474024821
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp
@@ -0,0 +1,73 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr iterator(iterator<!Const> i)
+// requires Const && convertible_to<iterator_t<V>, iterator_t<Base>>;
+
+#include <cassert>
+#include <ranges>
+#include <tuple>
+
+#include "../types.h"
+
+template <bool Const>
+struct ConvertibleIter : IterBase<ConvertibleIter<Const>> {
+ using iterator_category = std::random_access_iterator_tag;
+ using value_type = std::tuple<int>;
+ using
diff erence_type = intptr_t;
+
+ bool movedFromOtherConst = false;
+ int i = 0;
+
+ constexpr ConvertibleIter() = default;
+ constexpr ConvertibleIter(int ii) : i(ii) {}
+ template <bool otherConst>
+ requires(Const != otherConst)
+ constexpr ConvertibleIter(ConvertibleIter<otherConst> it) : movedFromOtherConst(true), i(it.i) {}
+};
+
+template <class Iter, class ConstIter>
+struct BasicView : std::ranges::view_base {
+ Iter begin();
+ Iter end();
+
+ ConstIter begin() const;
+ ConstIter end() const;
+};
+
+template <class View>
+using ElemIter = std::ranges::iterator_t<std::ranges::elements_view<View, 0>>;
+
+template <class View>
+using ConstElemIter = std::ranges::iterator_t<const std::ranges::elements_view<View, 0>>;
+
+using ConvertibleView = BasicView<ConvertibleIter<false>, ConvertibleIter<true>>;
+using NonConvertibleView = BasicView<forward_iterator<std::tuple<int>*>, bidirectional_iterator<std::tuple<int>*>>;
+
+static_assert(std::is_constructible_v<ConstElemIter<ConvertibleView>, ElemIter<ConvertibleView>>);
+static_assert(!std::is_constructible_v<ElemIter<ConvertibleView>, ConstElemIter<ConvertibleView>>);
+static_assert(!std::is_constructible_v<ConstElemIter<NonConvertibleView>, ElemIter<NonConvertibleView>>);
+static_assert(!std::is_constructible_v<ElemIter<NonConvertibleView>, ConstElemIter<NonConvertibleView>>);
+
+constexpr bool test() {
+ ElemIter<ConvertibleView> iter{ConvertibleIter<false>{5}};
+ ConstElemIter<ConvertibleView> constIter = iter; // implicit
+ assert(constIter.base().movedFromOtherConst);
+ assert(constIter.base().i == 5);
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/decrement.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/decrement.pass.cpp
new file mode 100644
index 0000000000000..f0bed5e1d0345
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/decrement.pass.cpp
@@ -0,0 +1,91 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr iterator& operator--() requires bidirectional_range<Base>;
+// constexpr iterator operator--(int) requires bidirectional_range<Base>;
+
+#include <array>
+#include <cassert>
+#include <ranges>
+#include <tuple>
+
+#include "test_iterators.h"
+
+template <class Iter>
+concept CanPreDecrement = requires(Iter it) { --it; };
+
+template <class Iter>
+concept CanPostDecrement = requires(Iter it) { it--; };
+
+template <class Iter, class Sent = sentinel_wrapper<Iter>>
+constexpr void testOne() {
+ using Range = std::ranges::subrange<Iter, Sent>;
+ std::tuple<int> ts[] = {{1}, {2}, {3}};
+ auto ev = Range{Iter{&ts[0]}, Sent{Iter{&ts[0] + 3}}} | std::views::elements<0>;
+
+ using ElementIter = std::ranges::iterator_t<decltype(ev)>;
+
+ if constexpr (!std::bidirectional_iterator<Iter>) {
+ auto it = ev.begin();
+ static_assert(!CanPreDecrement<decltype(it)>);
+ static_assert(!CanPostDecrement<decltype(it)>);
+ } else {
+ // --i
+ {
+ auto it = ev.begin();
+ static_assert(CanPreDecrement<decltype(it)>);
+
+ ++it;
+ assert(base(it.base()) == &ts[1]);
+
+ decltype(auto) result = --it;
+
+ static_assert(std::is_same_v<decltype(result), ElementIter&>);
+ assert(&result == &it);
+
+ assert(base(it.base()) == &ts[0]);
+ }
+
+ // i--
+ {
+ auto it = ev.begin();
+ static_assert(CanPostDecrement<decltype(it)>);
+
+ ++it;
+ assert(base(it.base()) == &ts[1]);
+
+ decltype(auto) result = it--;
+
+ static_assert(std::is_same_v<decltype(result), ElementIter>);
+
+ assert(base(it.base()) == &ts[0]);
+ assert(base(result.base()) == &ts[1]);
+ }
+ }
+}
+
+constexpr bool test() {
+ using Ptr = std::tuple<int>*;
+ testOne<cpp20_input_iterator<Ptr>>();
+ testOne<forward_iterator<Ptr>>();
+ testOne<bidirectional_iterator<Ptr>>();
+ testOne<random_access_iterator<Ptr>>();
+ testOne<contiguous_iterator<Ptr>>();
+ testOne<Ptr>();
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/deref.pass.cpp
new file mode 100644
index 0000000000000..d87a3e5339203
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/deref.pass.cpp
@@ -0,0 +1,100 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr decltype(auto) operator*() const;
+
+#include <array>
+#include <cassert>
+#include <ranges>
+#include <tuple>
+#include <utility>
+
+template <std::size_t N, class T, std::size_t Size>
+constexpr void testReference(T (&ts)[Size]) {
+ auto ev = ts | std::views::elements<N>;
+ auto it = ev.begin();
+
+ decltype(auto) result = *it;
+ using ExpectedType = decltype(std::get<N>(ts[0]));
+ static_assert(std::is_same_v<decltype(result), ExpectedType>);
+
+ if constexpr (std::is_reference_v<ExpectedType>) {
+ // tuple/array/pair
+ assert(&result == &std::get<N>(ts[0]));
+ } else {
+ // subrange
+ assert(result == std::get<N>(ts[0]));
+ }
+}
+
+// LWG 3502 elements_view should not be allowed to return dangling references
+template <std::size_t N, class T>
+constexpr void testValue(T t) {
+ auto ev = std::views::iota(0, 1) | std::views::transform([&t](int) { return t; }) | std::views::elements<N>;
+ auto it = ev.begin();
+
+ decltype(auto) result = *it;
+ using ExpectedType = std::remove_cvref_t<decltype(std::get<N>(t))>;
+ static_assert(std::is_same_v<decltype(result), ExpectedType>);
+
+ assert(result == std::get<N>(t));
+}
+
+constexpr bool test() {
+ // test tuple
+ {
+ std::tuple<int, short, long> ts[] = {{1, 2, 3}, {4, 5, 6}};
+ testReference<0>(ts);
+ testReference<1>(ts);
+ testReference<2>(ts);
+ testValue<0>(ts[0]);
+ testValue<1>(ts[0]);
+ testValue<2>(ts[0]);
+ }
+
+ // test pair
+ {
+ std::pair<int, short> ps[] = {{1, 2}, {4, 5}};
+ testReference<0>(ps);
+ testReference<1>(ps);
+ testValue<0>(ps[0]);
+ testValue<1>(ps[0]);
+ }
+
+ // test array
+ {
+ std::array<int, 3> arrs[] = {{1, 2, 3}, {3, 4, 5}};
+ testReference<0>(arrs);
+ testReference<1>(arrs);
+ testReference<2>(arrs);
+ testValue<0>(arrs[0]);
+ testValue<1>(arrs[0]);
+ testValue<2>(arrs[0]);
+ }
+
+ // test subrange
+ {
+ int i = 5;
+ std::ranges::subrange<int*> srs[] = {{&i, &i}, {&i, &i}};
+ testReference<0>(srs);
+ testReference<1>(srs);
+ testValue<0>(srs[0]);
+ testValue<1>(srs[0]);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/increment.pass.cpp
new file mode 100644
index 0000000000000..6b29ba0239e56
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/increment.pass.cpp
@@ -0,0 +1,78 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr iterator& operator++();
+// constexpr void operator++(int);
+// constexpr iterator operator++(int) requires forward_range<Base>;
+
+#include <array>
+#include <cassert>
+#include <ranges>
+#include <tuple>
+
+#include "test_iterators.h"
+
+template <class Iter, class Sent = sentinel_wrapper<Iter>>
+constexpr void testOne() {
+ using Range = std::ranges::subrange<Iter, Sent>;
+ std::tuple<int> ts[] = {{1}, {2}, {3}};
+ auto ev = Range{Iter{&ts[0]}, Sent{Iter{&ts[0] + 3}}} | std::views::elements<0>;
+
+ using ElementIter = std::ranges::iterator_t<decltype(ev)>;
+
+ // ++i
+ {
+ auto it = ev.begin();
+ decltype(auto) result = ++it;
+
+ static_assert(std::is_same_v<decltype(result), ElementIter&>);
+ assert(&result == &it);
+
+ assert(base(it.base()) == &ts[1]);
+ }
+
+ // i++
+ {
+ if constexpr (std::forward_iterator<Iter>) {
+ auto it = ev.begin();
+ decltype(auto) result = it++;
+
+ static_assert(std::is_same_v<decltype(result), ElementIter>);
+
+ assert(base(it.base()) == &ts[1]);
+ assert(base(result.base()) == &ts[0]);
+ } else {
+ auto it = ev.begin();
+ it++;
+
+ static_assert(std::is_same_v<decltype(it++), void>);
+ assert(base(it.base()) == &ts[1]);
+ }
+ }
+}
+
+constexpr bool test() {
+ using Ptr = std::tuple<int>*;
+ testOne<cpp20_input_iterator<Ptr>>();
+ testOne<forward_iterator<Ptr>>();
+ testOne<bidirectional_iterator<Ptr>>();
+ testOne<random_access_iterator<Ptr>>();
+ testOne<contiguous_iterator<Ptr>>();
+ testOne<Ptr>();
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/member_types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/member_types.compile.pass.cpp
new file mode 100644
index 0000000000000..67ec179b50b6b
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/member_types.compile.pass.cpp
@@ -0,0 +1,89 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Member typedefs in elements_view<V, N>::iterator.
+
+#include <concepts>
+#include <ranges>
+#include <tuple>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+template <class Iter>
+using Range = std::ranges::subrange<Iter, sentinel_wrapper<Iter>>;
+
+template <class Range, size_t N = 0>
+using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<Range, N>>;
+
+// using iterator_concept = see below;
+static_assert(std::same_as<ElementsIter<Range<cpp20_input_iterator<std::tuple<int>*>>>::iterator_concept,
+ std::input_iterator_tag>);
+
+static_assert(std::same_as<ElementsIter<Range<forward_iterator<std::tuple<int>*>>>::iterator_concept, //
+ std::forward_iterator_tag>);
+
+static_assert(std::same_as<ElementsIter<Range<bidirectional_iterator<std::tuple<int>*>>>::iterator_concept,
+ std::bidirectional_iterator_tag>);
+
+static_assert(std::same_as<ElementsIter<Range<random_access_iterator<std::tuple<int>*>>>::iterator_concept,
+ std::random_access_iterator_tag>);
+
+static_assert(std::same_as<ElementsIter<Range<contiguous_iterator<std::tuple<int>*>>>::iterator_concept,
+ std::random_access_iterator_tag>);
+
+static_assert(std::same_as<ElementsIter<Range<std::tuple<int>*>>::iterator_concept, //
+ std::random_access_iterator_tag>);
+
+// using iterator_category = see below; // not always present
+template <class T>
+concept HasIterCategory = requires { typename T::iterator_category; };
+
+static_assert(!HasIterCategory<ElementsIter<Range<cpp20_input_iterator<std::tuple<int>*>>>>);
+static_assert(HasIterCategory<ElementsIter<Range<forward_iterator<std::tuple<int>*>>>>);
+
+static_assert(std::same_as<ElementsIter<Range<forward_iterator<std::tuple<int>*>>>::iterator_category,
+ std::forward_iterator_tag>);
+
+static_assert(std::same_as<ElementsIter<Range<bidirectional_iterator<std::tuple<int>*>>>::iterator_category,
+ std::bidirectional_iterator_tag>);
+
+static_assert(std::same_as<ElementsIter<Range<random_access_iterator<std::tuple<int>*>>>::iterator_category,
+ std::random_access_iterator_tag>);
+
+static_assert(std::same_as<ElementsIter<Range<contiguous_iterator<std::tuple<int>*>>>::iterator_category,
+ std::random_access_iterator_tag>);
+
+static_assert(std::same_as<ElementsIter<Range<std::tuple<int>*>>::iterator_category, //
+ std::random_access_iterator_tag>);
+
+using Generator = decltype(std::views::iota(0, 1) | std::views::transform([](int) {
+ return std::pair<int, short>{1, 1};
+ }));
+static_assert(std::ranges::random_access_range<Generator>);
+
+static_assert(std::same_as<ElementsIter<Generator>::iterator_category, //
+ std::input_iterator_tag>);
+
+// using value_type = remove_cvref_t<tuple_element_t<N, range_value_t<Base>>>;
+static_assert(std::same_as<ElementsIter<Range<std::tuple<int, long>*>, 0>::value_type, int>);
+
+static_assert(std::same_as<ElementsIter<Range<std::tuple<int, long>*>, 1>::value_type, long>);
+
+static_assert(std::same_as<ElementsIter<Generator, 0>::value_type, int>);
+
+static_assert(std::same_as<ElementsIter<Generator, 1>::value_type, short>);
+
+// using
diff erence_type = range_
diff erence_t<Base>;
+static_assert(std::same_as<ElementsIter<Range<std::tuple<int, long>*>>::
diff erence_type,
+ std::ranges::range_
diff erence_t<Range<std::tuple<int, long>*>>>);
+
+static_assert(std::same_as<ElementsIter<Generator>::
diff erence_type, //
+ std::ranges::range_
diff erence_t<Generator>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/subscript.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/subscript.pass.cpp
new file mode 100644
index 0000000000000..e456ead0e4c52
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/subscript.pass.cpp
@@ -0,0 +1,69 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr decltype(auto) operator[](
diff erence_type n) const
+// requires random_access_range<Base>
+
+#include <cassert>
+#include <ranges>
+#include <tuple>
+
+#include "test_iterators.h"
+
+template <class T, class U>
+concept CanSubscript = requires(T t, U u) { t[u]; };
+
+template <class BaseRange>
+using ElemIter = std::ranges::iterator_t<std::ranges::elements_view<BaseRange, 0>>;
+
+using RandomAccessRange = std::ranges::subrange<std::tuple<int>*>;
+static_assert(std::ranges::random_access_range<RandomAccessRange>);
+
+static_assert(CanSubscript<ElemIter<RandomAccessRange>, int>);
+
+using BidiRange = std::ranges::subrange<bidirectional_iterator<std::tuple<int>*>>;
+static_assert(!std::ranges::random_access_range<BidiRange>);
+
+static_assert(!CanSubscript<ElemIter<BidiRange>, int>);
+
+constexpr bool test() {
+ {
+ // reference
+ std::tuple<int> ts[] = {{1}, {2}, {3}, {4}};
+ auto ev = ts | std::views::elements<0>;
+ auto it = ev.begin();
+
+ assert(&it[0] == &*it);
+ assert(&it[2] == &*(it + 2));
+
+ static_assert(std::is_same_v<decltype(it[2]), int&>);
+ }
+
+ {
+ // value
+ auto ev = std::views::iota(0, 5) | std::views::transform([](int i) { return std::tuple<int>{i}; }) |
+ std::views::elements<0>;
+ auto it = ev.begin();
+ assert(it[0] == *it);
+ assert(it[2] == *(it + 2));
+ assert(it[4] == *(it + 4));
+
+ static_assert(std::is_same_v<decltype(it[2]), int>);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/range.concept.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/range.concept.compile.pass.cpp
new file mode 100644
index 0000000000000..021f0283b4336
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/range.concept.compile.pass.cpp
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// concept checking
+//
+// template<class T, size_t N>
+// concept has-tuple-element =
+// tuple-like<T> && N < tuple_size_v<T>;
+//
+// template<class T, size_t N>
+// concept returnable-element =
+// is_reference_v<T> || move_constructible<tuple_element_t<N, T>>;
+//
+// template<input_range V, size_t N>
+// requires view<V> && has-tuple-element<range_value_t<V>, N> &&
+// has-tuple-element<remove_reference_t<range_reference_t<V>>, N> &&
+// returnable-element<range_reference_t<V>, N>
+// class elements_view;
+
+#include <array>
+#include <concepts>
+#include <tuple>
+#include <ranges>
+#include <utility>
+
+#include "test_iterators.h"
+
+template <class It>
+using Range = std::ranges::subrange<It, sentinel_wrapper<It>>;
+
+template <class V, size_t N>
+concept HasElementsView = requires { typename std::ranges::elements_view<V, N>; };
+
+static_assert(HasElementsView<Range<std::ranges::subrange<int*>*>, 0>);
+static_assert(HasElementsView<Range<std::pair<int, int>*>, 1>);
+static_assert(HasElementsView<Range<std::tuple<int, int, int>*>, 2>);
+static_assert(HasElementsView<Range<std::array<int, 4>*>, 3>);
+
+// !view<V>
+static_assert(!std::ranges::view<std::array<std::tuple<int>, 1>>);
+static_assert(!HasElementsView<std::array<std::tuple<int>, 1>, 0>);
+
+// !input_range
+static_assert(!std::ranges::input_range<Range<cpp20_output_iterator<std::tuple<int>*>>>);
+static_assert(!HasElementsView<Range<cpp20_output_iterator<std::tuple<int>*>>, 0>);
+
+// !tuple-like
+LIBCPP_STATIC_ASSERT(!std::__tuple_like<int>);
+static_assert(!HasElementsView<Range<int*>, 1>);
+
+// !(N < tuple_size_v<T>)
+static_assert(!(2 < std::tuple_size_v< std::pair<int, int>>));
+static_assert(!HasElementsView<Range<std::pair<int, int>*>, 2>);
+
+// ! (is_reference_v<T> || move_constructible<tuple_element_t<N, T>>)
+struct NonMovable {
+ NonMovable(int) {}
+ NonMovable(NonMovable&&) = delete;
+};
+static_assert(!std::move_constructible<NonMovable>);
+
+using NonMovableGenerator =
+ decltype(std::views::iota(0, 1) | std::views::transform([](int) {
+ return std::pair<NonMovable, int>{1, 1};
+ }));
+
+static_assert(!HasElementsView<NonMovableGenerator, 0>);
+static_assert(HasElementsView<NonMovableGenerator, 1>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
new file mode 100644
index 0000000000000..d4d0156fdb5be
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp
@@ -0,0 +1,41 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr sentinel_t<Base> base() const;
+
+#include <cassert>
+#include <ranges>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+struct Sent {
+ int i;
+
+ friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
+};
+
+constexpr bool test() {
+ using BaseRange = std::ranges::subrange<std::tuple<int>*, Sent>;
+ using EleRange = std::ranges::elements_view<BaseRange, 0>;
+ using EleSent = std::ranges::sentinel_t<EleRange>;
+
+ const EleSent st{Sent{5}};
+ std::same_as<Sent> decltype(auto) base = st.base();
+ assert(base.i == 5);
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp
new file mode 100644
index 0000000000000..13fef1d53f121
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp
@@ -0,0 +1,52 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr explicit sentinel(sentinel_t<Base> end);
+
+#include <cassert>
+#include <ranges>
+#include <tuple>
+#include <utility>
+
+struct Sent {
+ int i;
+
+ friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
+};
+
+struct Range : std::ranges::view_base {
+ std::tuple<int>* begin() const;
+ Sent end();
+};
+
+// Test explicit
+
+static_assert(std::is_constructible_v<std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>, Sent>);
+static_assert(!std::is_convertible_v<Sent, std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>>);
+
+constexpr bool test() {
+ // base is init correctly
+ {
+ using R = std::ranges::elements_view<Range, 0>;
+ using Sentinel = std::ranges::sentinel_t<R>;
+
+ Sentinel s1(Sent{5});
+ assert(s1.base().i == 5);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
new file mode 100644
index 0000000000000..4ff65be645220
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr sentinel(sentinel<!Const> s)
+// requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>;
+
+#include <cassert>
+#include <ranges>
+#include <tuple>
+
+#include "../types.h"
+
+struct Sent {
+ int i;
+ constexpr Sent() = default;
+ constexpr Sent(int ii) : i(ii) {}
+ friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; }
+};
+
+struct ConstSent {
+ int i;
+ constexpr ConstSent() = default;
+ constexpr ConstSent(int ii) : i(ii) {}
+ constexpr ConstSent(const Sent& s) : i(s.i) {}
+ friend constexpr bool operator==(std::tuple<int>*, const ConstSent&) { return true; }
+};
+
+struct Range : std::ranges::view_base {
+ std::tuple<int>* begin() const;
+ Sent end();
+ ConstSent end() const;
+};
+
+struct NonConvertConstSent {
+ int i;
+ constexpr NonConvertConstSent() = default;
+ constexpr NonConvertConstSent(int ii) : i(ii) {}
+ friend constexpr bool operator==(std::tuple<int>*, const NonConvertConstSent&) { return true; }
+};
+
+struct NonConvertConstSentRange : std::ranges::view_base {
+ std::tuple<int>* begin() const;
+ Sent end();
+ NonConvertConstSent end() const;
+};
+
+// Test Constraint
+static_assert(std::is_constructible_v<std::ranges::sentinel_t<const std::ranges::elements_view<Range, 0>>,
+ std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>>);
+
+// !Const
+static_assert(!std::is_constructible_v<std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>,
+ std::ranges::sentinel_t<const std::ranges::elements_view<Range, 0>>>);
+
+// !convertible_to<sentinel_t<V>, sentinel_t<Base>>
+static_assert(!std::is_constructible_v<
+ std::ranges::sentinel_t<const std::ranges::elements_view<NonConvertConstSentRange, 0>>,
+ std::ranges::sentinel_t<std::ranges::elements_view<NonConvertConstSentRange, 0>>>);
+
+constexpr bool test() {
+ // base is init correctly
+ {
+ using R = std::ranges::elements_view<Range, 0>;
+ using Sentinel = std::ranges::sentinel_t<R>;
+ using ConstSentinel = std::ranges::sentinel_t<const R>;
+ static_assert(!std::same_as<Sentinel, ConstSentinel>);
+
+ Sentinel s1(Sent{5});
+ ConstSentinel s2 = s1;
+ assert(s2.base().i == 5);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.default.pass.cpp
new file mode 100644
index 0000000000000..b2f4eae51baee
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.default.pass.cpp
@@ -0,0 +1,49 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// sentinel() = default;
+
+#include <cassert>
+#include <ranges>
+#include <tuple>
+
+struct PODSentinel {
+ int i; // deliberately uninitialised
+
+ friend constexpr bool operator==(std::tuple<int>*, const PODSentinel&) { return true; }
+};
+
+struct Range : std::ranges::view_base {
+ std::tuple<int>* begin() const;
+ PODSentinel end();
+};
+
+constexpr bool test() {
+ using EleView = std::ranges::elements_view<Range, 0>;
+ using Sentinel = std::ranges::sentinel_t<EleView>;
+ static_assert(!std::is_same_v<Sentinel, std::ranges::iterator_t<EleView>>);
+
+ {
+ Sentinel s;
+ assert(s.base().i == 0);
+ }
+ {
+ Sentinel s = {};
+ assert(s.base().i == 0);
+ }
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/equality.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/equality.pass.cpp
new file mode 100644
index 0000000000000..55477cc997587
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/equality.pass.cpp
@@ -0,0 +1,164 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// template<bool OtherConst>
+// requires sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>>
+// friend constexpr bool operator==(const iterator<OtherConst>& x, const sentinel& y);
+
+#include <array>
+#include <cassert>
+#include <ranges>
+
+#include "../types.h"
+
+template <bool Const>
+struct Iter {
+ std::tuple<int>* it_;
+
+ using value_type = std::tuple<int>;
+ using
diff erence_type = intptr_t;
+ using iterator_concept = std::input_iterator_tag;
+
+ constexpr decltype(auto) operator*() const { return *it_; }
+ constexpr Iter& operator++() {
+ ++it_;
+ return *this;
+ }
+ constexpr void operator++(int) { ++it_; }
+};
+
+template <bool Const>
+struct Sent {
+ std::tuple<int>* end_;
+
+ constexpr bool operator==(const Iter<Const>& i) const { return i.it_ == end_; }
+};
+
+template <bool Const>
+struct CrossComparableSent {
+ std::tuple<int>* end_;
+
+ template <bool C>
+ constexpr bool operator==(const Iter<C>& i) const {
+ return i.it_ == end_;
+ }
+};
+
+template <template <bool> typename St>
+struct Range : TupleBufferView {
+ using TupleBufferView::TupleBufferView;
+ constexpr Iter<false> begin() { return Iter<false>{buffer_}; }
+ constexpr Iter<true> begin() const { return Iter<true>{buffer_}; }
+ constexpr St<false> end() { return St<false>{buffer_ + size_}; }
+ constexpr St<true> end() const { return St<true>{buffer_ + size_}; }
+};
+
+using R = Range<Sent>;
+using CrossComparableR = Range<CrossComparableSent>;
+
+// Test Constraint
+template <class I, class S>
+concept HasEqual = requires(const I i, const S s) { i == s; };
+
+using std::ranges::elements_view;
+using std::ranges::iterator_t;
+using std::ranges::sentinel_t;
+
+static_assert(HasEqual<iterator_t<elements_view<R, 0>>, //
+ sentinel_t<elements_view<R, 0>>>);
+
+static_assert(!HasEqual<iterator_t<const elements_view<R, 0>>, //
+ sentinel_t<elements_view<R, 0>>>);
+
+static_assert(!HasEqual<iterator_t<elements_view<R, 0>>, //
+ sentinel_t<const elements_view<R, 0>>>);
+
+static_assert(HasEqual<iterator_t<const elements_view<R, 0>>, //
+ sentinel_t<const elements_view<R, 0>>>);
+
+static_assert(HasEqual<iterator_t<elements_view<R, 0>>, //
+ sentinel_t<elements_view<R, 0>>>);
+
+static_assert(HasEqual<iterator_t<const elements_view<CrossComparableR, 0>>, //
+ sentinel_t<elements_view<CrossComparableR, 0>>>);
+
+static_assert(HasEqual<iterator_t<elements_view<CrossComparableR, 0>>, //
+ sentinel_t<const elements_view<CrossComparableR, 0>>>);
+
+static_assert(HasEqual<iterator_t<const elements_view<CrossComparableR, 0>>, //
+ sentinel_t<const elements_view<CrossComparableR, 0>>>);
+
+template <class R, bool ConstIter, bool ConstSent>
+constexpr void testOne() {
+ auto getBegin = [](auto&& rng) {
+ if constexpr (ConstIter) {
+ return std::as_const(rng).begin();
+ } else {
+ return rng.begin();
+ }
+ };
+
+ auto getEnd = [](auto&& rng) {
+ if constexpr (ConstSent) {
+ return std::as_const(rng).end();
+ } else {
+ return rng.end();
+ }
+ };
+
+ // iter == sentinel.base
+ {
+ std::tuple<int> buffer[] = {{1}};
+ R v{buffer};
+ std::ranges::elements_view<R, 0> ev(v);
+ auto iter = getBegin(ev);
+ auto st = getEnd(ev);
+ ++iter;
+ assert(iter == st);
+ }
+
+ // iter != sentinel.base
+ {
+ std::tuple<int> buffer[] = {{1}};
+ R v{buffer};
+ std::ranges::elements_view<R, 0> ev(v);
+ auto iter = getBegin(ev);
+ auto st = getEnd(ev);
+ assert(iter != st);
+ }
+
+ // empty range
+ {
+ std::array<std::tuple<int>, 0> arr;
+ R v{arr};
+ std::ranges::elements_view<R, 0> ev(v);
+ auto iter = getBegin(ev);
+ auto sent = getEnd(ev);
+ assert(iter == sent);
+ }
+}
+
+constexpr bool test() {
+ testOne<R, false, false>();
+ testOne<R, true, true>();
+ testOne<CrossComparableR, false, false>();
+ testOne<CrossComparableR, true, true>();
+ testOne<CrossComparableR, true, false>();
+ testOne<CrossComparableR, false, true>();
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/minus.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/minus.pass.cpp
new file mode 100644
index 0000000000000..9af7b097aa4f2
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/minus.pass.cpp
@@ -0,0 +1,213 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// template<bool OtherConst>
+// requires sized_sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>>
+// friend constexpr range_
diff erence_t<maybe-const<OtherConst, V>>
+// operator-(const iterator<OtherConst>& x, const sentinel& y);
+//
+// template<bool OtherConst>
+// requires sized_sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>>
+// friend constexpr range_
diff erence_t<maybe-const<OtherConst, V>>
+// operator-(const sentinel& x, const iterator<OtherConst>& y);
+
+#include <cassert>
+#include <concepts>
+#include <functional>
+#include <ranges>
+#include <tuple>
+
+#include "../types.h"
+
+template <bool Const>
+struct Iter {
+ std::tuple<int>* it_;
+
+ using value_type = std::tuple<int>;
+ using
diff erence_type = intptr_t;
+ using iterator_concept = std::input_iterator_tag;
+
+ constexpr decltype(auto) operator*() const { return *it_; }
+ constexpr Iter& operator++() {
+ ++it_;
+ return *this;
+ }
+ constexpr void operator++(int) { ++it_; }
+};
+
+template <bool Const>
+struct Sent {
+ std::tuple<int>* end_;
+
+ constexpr bool operator==(const Iter<Const>& i) const { return i.it_ == end_; }
+};
+
+template <bool Const>
+struct SizedSent {
+ std::tuple<int>* end_;
+
+ constexpr bool operator==(const Iter<Const>& i) const { return i.it_ == end_; }
+
+ friend constexpr auto operator-(const SizedSent& st, const Iter<Const>& it) { return st.end_ - it.it_; }
+
+ friend constexpr auto operator-(const Iter<Const>& it, const SizedSent& st) { return it.it_ - st.end_; }
+};
+
+template <bool Const>
+struct CrossSizedSent {
+ std::tuple<int>* end_;
+
+ template <bool C>
+ constexpr bool operator==(const Iter<C>& i) const {
+ return i.it_ == end_;
+ }
+
+ template <bool C>
+ friend constexpr auto operator-(const CrossSizedSent& st, const Iter<C>& it) {
+ return st.end_ - it.it_;
+ }
+
+ template <bool C>
+ friend constexpr auto operator-(const Iter<C>& it, const CrossSizedSent& st) {
+ return it.it_ - st.end_;
+ }
+};
+
+template <template <bool> class It, template <bool> class St>
+struct Range : TupleBufferView {
+ using TupleBufferView::TupleBufferView;
+
+ using iterator = It<false>;
+ using sentinel = St<false>;
+ using const_iterator = It<true>;
+ using const_sentinel = St<true>;
+
+ constexpr iterator begin() { return {buffer_}; }
+ constexpr const_iterator begin() const { return {buffer_}; }
+ constexpr sentinel end() { return sentinel{buffer_ + size_}; }
+ constexpr const_sentinel end() const { return const_sentinel{buffer_ + size_}; }
+};
+
+template <class T, class U>
+concept HasMinus = requires(const T t, const U u) { t - u; };
+
+template <class BaseRange>
+using ElementsView = std::ranges::elements_view<BaseRange, 0>;
+
+template <class BaseRange>
+using ElemIter = std::ranges::iterator_t<ElementsView<BaseRange>>;
+
+template <class BaseRange>
+using EleConstIter = std::ranges::iterator_t<const ElementsView<BaseRange>>;
+
+template <class BaseRange>
+using EleSent = std::ranges::sentinel_t<ElementsView<BaseRange>>;
+
+template <class BaseRange>
+using EleConstSent = std::ranges::sentinel_t<const ElementsView<BaseRange>>;
+
+constexpr void testConstraints() {
+ // base is not sized
+ {
+ using Base = Range<Iter, Sent>;
+ static_assert(!HasMinus<EleSent<Base>, ElemIter<Base>>);
+ static_assert(!HasMinus<ElemIter<Base>, EleSent<Base>>);
+
+ static_assert(!HasMinus<EleSent<Base>, EleConstIter<Base>>);
+ static_assert(!HasMinus<EleConstIter<Base>, EleSent<Base>>);
+
+ static_assert(!HasMinus<EleConstSent<Base>, EleConstIter<Base>>);
+ static_assert(!HasMinus<EleConstIter<Base>, EleConstSent<Base>>);
+
+ static_assert(!HasMinus<EleConstSent<Base>, ElemIter<Base>>);
+ static_assert(!HasMinus<ElemIter<Base>, EleConstSent<Base>>);
+ }
+
+ // base is sized but not cross const
+ {
+ using Base = Range<Iter, SizedSent>;
+ static_assert(HasMinus<EleSent<Base>, ElemIter<Base>>);
+ static_assert(HasMinus<ElemIter<Base>, EleSent<Base>>);
+
+ static_assert(!HasMinus<EleSent<Base>, EleConstIter<Base>>);
+ static_assert(!HasMinus<EleConstIter<Base>, EleSent<Base>>);
+
+ static_assert(HasMinus<EleConstSent<Base>, EleConstIter<Base>>);
+ static_assert(HasMinus<EleConstIter<Base>, EleConstSent<Base>>);
+
+ static_assert(!HasMinus<EleConstSent<Base>, ElemIter<Base>>);
+ static_assert(!HasMinus<ElemIter<Base>, EleConstSent<Base>>);
+ }
+
+ // base is cross const sized
+ {
+ using Base = Range<Iter, CrossSizedSent>;
+ static_assert(HasMinus<EleSent<Base>, ElemIter<Base>>);
+ static_assert(HasMinus<ElemIter<Base>, EleSent<Base>>);
+
+ static_assert(HasMinus<EleSent<Base>, EleConstIter<Base>>);
+ static_assert(HasMinus<EleConstIter<Base>, EleSent<Base>>);
+
+ static_assert(HasMinus<EleConstSent<Base>, EleConstIter<Base>>);
+ static_assert(HasMinus<EleConstIter<Base>, EleConstSent<Base>>);
+
+ static_assert(HasMinus<EleConstSent<Base>, ElemIter<Base>>);
+ static_assert(HasMinus<ElemIter<Base>, EleConstSent<Base>>);
+ }
+}
+
+constexpr bool test() {
+ std::tuple<int> buffer[] = {{1}, {2}, {3}, {4}, {5}};
+
+ // base is sized but not cross const
+ {
+ using Base = Range<Iter, SizedSent>;
+ Base base{buffer};
+ auto ev = base | std::views::elements<0>;
+ auto iter = ev.begin();
+ auto const_iter = std::as_const(ev).begin();
+ auto sent = ev.end();
+ auto const_sent = std::as_const(ev).end();
+
+ assert(iter - sent == -5);
+ assert(sent - iter == 5);
+ assert(const_iter - const_sent == -5);
+ assert(const_sent - const_iter == 5);
+ }
+
+ // base is cross const sized
+ {
+ using Base = Range<Iter, CrossSizedSent>;
+ Base base{buffer};
+ auto ev = base | std::views::elements<0>;
+ auto iter = ev.begin();
+ auto const_iter = std::as_const(ev).begin();
+ auto sent = ev.end();
+ auto const_sent = std::as_const(ev).end();
+
+ assert(iter - sent == -5);
+ assert(sent - iter == 5);
+ assert(iter - const_sent == -5);
+ assert(const_sent - iter == 5);
+ assert(const_iter - sent == -5);
+ assert(sent - const_iter == 5);
+ assert(const_iter - const_sent == -5);
+ assert(const_sent - const_iter == 5);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/size.pass.cpp
new file mode 100644
index 0000000000000..027cb935506fe
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/size.pass.cpp
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto size() requires sized_range<V>
+// constexpr auto size() const requires sized_range<const V>
+
+#include <cassert>
+#include <ranges>
+#include <tuple>
+#include <utility>
+
+#include "types.h"
+
+template <class T>
+concept HasSize = requires(T t) { t.size(); };
+
+static_assert(HasSize<std::ranges::elements_view<SimpleCommon, 0>>);
+static_assert(HasSize<const std::ranges::elements_view<SimpleCommon, 0>>);
+
+struct NonSized : std::ranges::view_base {
+ using iterator = forward_iterator<std::tuple<int>*>;
+ iterator begin() const;
+ iterator end() const;
+};
+static_assert(!std::ranges::sized_range<NonSized>);
+static_assert(!std::ranges::sized_range<const NonSized>);
+
+static_assert(!HasSize<std::ranges::elements_view<NonSized, 0>>);
+static_assert(!HasSize<const std::ranges::elements_view<NonSized, 0>>);
+
+struct SizedNonConst : TupleBufferView {
+ using TupleBufferView::TupleBufferView;
+
+ using iterator = forward_iterator<std::tuple<int>*>;
+ constexpr auto begin() const { return iterator{buffer_}; }
+ constexpr auto end() const { return iterator{buffer_ + size_}; }
+ constexpr std::size_t size() { return size_; }
+};
+
+static_assert(HasSize<std::ranges::elements_view<SizedNonConst, 0>>);
+static_assert(!HasSize<const std::ranges::elements_view<SizedNonConst, 0>>);
+
+struct OnlyConstSized : TupleBufferView {
+ using TupleBufferView::TupleBufferView;
+
+ using iterator = forward_iterator<std::tuple<int>*>;
+ constexpr auto begin() const { return iterator{buffer_}; }
+ constexpr auto end() const { return iterator{buffer_ + size_}; }
+ constexpr std::size_t size() const { return size_; }
+ constexpr std::size_t size() = delete;
+};
+
+static_assert(HasSize<const OnlyConstSized>);
+static_assert(HasSize<std::ranges::elements_view<OnlyConstSized, 0>>);
+static_assert(HasSize<const std::ranges::elements_view<OnlyConstSized, 0>>);
+
+constexpr bool test() {
+ std::tuple<int> buffer[] = {{1}, {2}, {3}};
+
+ // non-const and const are sized
+ {
+ auto ev = std::views::elements<0>(buffer);
+ assert(ev.size() == 3);
+ assert(std::as_const(ev).size() == 3);
+ }
+
+ {
+ // const-view non-sized range
+ auto ev = std::views::elements<0>(SizedNonConst{buffer});
+ assert(ev.size() == 3);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/types.h b/libcxx/test/std/ranges/range.adaptors/range.elements/types.h
new file mode 100644
index 0000000000000..a1c0884b60719
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.elements/types.h
@@ -0,0 +1,106 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ELEMENTS_TYPES_H
+#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ELEMENTS_TYPES_H
+
+#include <array>
+#include <functional>
+#include <ranges>
+#include <tuple>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "test_range.h"
+
+template <class T>
+struct BufferView : std::ranges::view_base {
+ T* buffer_;
+ std::size_t size_;
+
+ template <std::size_t N>
+ constexpr BufferView(T (&b)[N]) : buffer_(b), size_(N) {}
+
+ template <std::size_t N>
+ constexpr BufferView(std::array<T, N>& arr) : buffer_(arr.data()), size_(N) {}
+};
+
+using TupleBufferView = BufferView<std::tuple<int>>;
+
+template <bool Simple>
+struct Common : TupleBufferView {
+ using TupleBufferView::TupleBufferView;
+
+ constexpr std::tuple<int>* begin()
+ requires(!Simple)
+ {
+ return buffer_;
+ }
+ constexpr const std::tuple<int>* begin() const { return buffer_; }
+ constexpr std::tuple<int>* end()
+ requires(!Simple)
+ {
+ return buffer_ + size_;
+ }
+ constexpr const std::tuple<int>* end() const { return buffer_ + size_; }
+};
+using SimpleCommon = Common<true>;
+using NonSimpleCommon = Common<false>;
+
+using SimpleCommonRandomAccessSized = SimpleCommon;
+using NonSimpleCommonRandomAccessSized = NonSimpleCommon;
+
+static_assert(std::ranges::common_range<Common<true>>);
+static_assert(std::ranges::random_access_range<SimpleCommon>);
+static_assert(std::ranges::sized_range<SimpleCommon>);
+LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<SimpleCommon>);
+LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleCommon>);
+
+template <bool Simple>
+struct NonCommon : TupleBufferView {
+ using TupleBufferView::TupleBufferView;
+ constexpr std::tuple<int>* begin()
+ requires(!Simple)
+ {
+ return buffer_;
+ }
+ constexpr const std::tuple<int>* begin() const { return buffer_; }
+ constexpr sentinel_wrapper<std::tuple<int>*> end()
+ requires(!Simple)
+ {
+ return sentinel_wrapper<std::tuple<int>*>(buffer_ + size_);
+ }
+ constexpr sentinel_wrapper<const std::tuple<int>*> end() const {
+ return sentinel_wrapper<const std::tuple<int>*>(buffer_ + size_);
+ }
+};
+
+using SimpleNonCommon = NonCommon<true>;
+using NonSimpleNonCommon = NonCommon<false>;
+
+static_assert(!std::ranges::common_range<SimpleNonCommon>);
+static_assert(std::ranges::random_access_range<SimpleNonCommon>);
+static_assert(!std::ranges::sized_range<SimpleNonCommon>);
+LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<SimpleNonCommon>);
+LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleNonCommon>);
+
+template <class Derived>
+struct IterBase {
+ using iterator_concept = std::random_access_iterator_tag;
+ using value_type = std::tuple<int>;
+ using
diff erence_type = intptr_t;
+
+ constexpr std::tuple<int> operator*() const { return std::tuple<int>(5); }
+
+ constexpr Derived& operator++() { return *this; }
+ constexpr void operator++(int) {}
+
+ friend constexpr bool operator==(const IterBase&, const IterBase&) = default;
+};
+
+#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ELEMENTS_TYPES_H
diff --git a/llvm/utils/gn/secondary/libcxx/include/BUILD.gn b/llvm/utils/gn/secondary/libcxx/include/BUILD.gn
index 9d9fda0e3efc5..ffd2d822cc472 100644
--- a/llvm/utils/gn/secondary/libcxx/include/BUILD.gn
+++ b/llvm/utils/gn/secondary/libcxx/include/BUILD.gn
@@ -629,10 +629,12 @@ if (current_toolchain == default_toolchain) {
"__tree",
"__tuple_dir/apply_cv.h",
"__tuple_dir/make_tuple_types.h",
+ "__tuple_dir/pair_like.h",
"__tuple_dir/sfinae_helpers.h",
"__tuple_dir/tuple_element.h",
"__tuple_dir/tuple_indices.h",
"__tuple_dir/tuple_like.h",
+ "__tuple_dir/tuple_like_ext.h",
"__tuple_dir/tuple_size.h",
"__tuple_dir/tuple_types.h",
"__type_traits/add_const.h",
More information about the libcxx-commits
mailing list