[libcxx-commits] [libcxx] WIP [libc++][ranges] `std::views::as_input` (PR #151449)
Hristo Hristov via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Aug 4 03:38:41 PDT 2026
https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/151449
>From 8f5dc79900d6cdb09c241e58bcbe5f6009d817a6 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 9 Jul 2025 19:25:52 +0300
Subject: [PATCH] [libc++][ranges] P3137R3: `views::to_input`
Implements std::views::as_input:
- https://wg21.link/P3137R3 P3137R3: views::to_input
- https://wg21.link/P3828R1 P3828R1: Rename the to_input view to as_input
Closes #127873
Closes #189602
References:
- https://wg21.link/range.as.input
- https://wg21.link/range.as.input
---
libcxx/docs/FeatureTestMacroTable.rst | 2 +
libcxx/docs/ReleaseNotes/24.rst | 2 +
libcxx/docs/Status/Cxx26Papers.csv | 4 +-
libcxx/include/CMakeLists.txt | 1 +
libcxx/include/__ranges/as_input_view.h | 224 ++++++++++++++
libcxx/include/module.modulemap.in | 3 +
libcxx/include/ranges | 12 +
libcxx/include/version | 2 +
libcxx/modules/std/ranges.inc | 10 +
.../range.as.input/nodiscard.verify.cpp | 90 ++++++
.../ranges.version.compile.pass.cpp | 27 ++
.../version.version.compile.pass.cpp | 27 ++
.../cpo.compile.pass.cpp | 2 +-
.../range.as.input/adaptor.pass.cpp | 278 ++++++++++++++++++
.../range.as.input/base.pass.cpp | 86 ++++++
.../range.as.input/begin.pass.cpp | 32 ++
.../range.as.input/ctad.compile.pass.cpp | 32 ++
.../range.as.input/ctor.default.pass.cpp | 83 ++++++
.../range.as.input/ctor.view.pass.cpp | 26 ++
.../enable_borrowed_range.compile.pass.cpp | 38 +++
.../range.as.input/end.pass.cpp | 31 ++
.../range.as.input/general.pass.cpp | 60 ++++
.../range.as.input/iterator/base.pass.cpp | 167 +++++++++++
.../iterator/ctor.converting.pass.cpp | 73 +++++
.../iterator/ctor.default.pass.cpp | 83 ++++++
.../iterator/ctor.move.pass.cpp | 120 ++++++++
.../iterator/iter_move.pass.cpp | 33 +++
.../iterator/iter_swap.pass.cpp | 34 +++
.../range.as.input/iterator/op.deref.pass.cpp | 32 ++
.../range.as.input/iterator/op.eq.pass.cpp | 32 ++
.../iterator/op.increment.pass.cpp | 33 +++
.../range.as.input/iterator/op.minus.pass.cpp | 35 +++
.../range.as.input/size.pass.cpp | 111 +++++++
.../generate_feature_test_macro_components.py | 5 +
34 files changed, 1827 insertions(+), 3 deletions(-)
create mode 100644 libcxx/include/__ranges/as_input_view.h
create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.as.input/nodiscard.verify.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.input/adaptor.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.input/base.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.input/begin.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.input/ctad.compile.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.input/ctor.default.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.input/ctor.view.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.input/enable_borrowed_range.compile.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.input/end.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.input/general.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/base.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/ctor.converting.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/ctor.default.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/ctor.move.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/iter_move.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/iter_swap.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/op.deref.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/op.eq.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/op.increment.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/op.minus.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.as.input/size.pass.cpp
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 60af581e122ec..23c47b3608bf0 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -510,6 +510,8 @@ Status
---------------------------------------------------------- -----------------
``__cpp_lib_philox_engine`` *unimplemented*
---------------------------------------------------------- -----------------
+ ``__cpp_lib_ranges_as_input`` ``202502L``
+ ---------------------------------------------------------- -----------------
``__cpp_lib_ranges_concat`` ``202403L``
---------------------------------------------------------- -----------------
``__cpp_lib_ranges_indices`` ``202506L``
diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index f42b5621e9fb5..e5404bd4dc3ea 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -40,6 +40,8 @@ Implemented Papers
- P0493R5: Atomic minimum/maximum (`Github <https://llvm.org/PR105418>`__)
- P3059R2: Making user-defined constructors of view iterators/sentinels private (`Github <https://github.com/llvm/llvm-project/issues/189599>`__)
+- P3137R3: ``views::as_input`` (`Github <https://llvm.org/PR127873>`__)
+- P3828R1: Rename the ``as_input`` view to ``as_input`` (`Github <https://llvm.org/PR189602>`__)
- P0792R14: ``function_ref`` : a type-erased callable reference (`Github <https://llvm.org/PR105376>`__)
- P3948R1: ``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments (`Github <https://llvm.org/PR189604>`__)
- P3961R1: Less double indirection in ``function_ref`` (RU-220) (`Github <https://llvm.org/PR189606>`__)
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index 1832ec915a06a..b62236260ef7b 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -101,7 +101,7 @@
"`P2900R14 <https://wg21.link/P2900R14>`__","Contracts for C++","2025-02 (Hagenberg)","","","`#127870 <https://github.com/llvm/llvm-project/issues/127870>`__",""
"`P3475R2 <https://wg21.link/P3475R2>`__","Defang and deprecate ``memory_order::consume``","2025-02 (Hagenberg)","","","`#127871 <https://github.com/llvm/llvm-project/issues/127871>`__",""
"`P2786R13 <https://wg21.link/P2786R13>`__","Trivial Relocatability For C++26","2025-02 (Hagenberg)","|Nothing To Do|","","`#127872 <https://github.com/llvm/llvm-project/issues/127872>`__","Reverted by `P3920R0 <https://wg21.link/P3920R0>`__"
-"`P3137R3 <https://wg21.link/P3137R3>`__","``views::to_input``","2025-02 (Hagenberg)","","","`#127873 <https://github.com/llvm/llvm-project/issues/127873>`__",""
+"`P3137R3 <https://wg21.link/P3137R3>`__","``views::to_input``","2025-02 (Hagenberg)","|Complete|","24","`#127873 <https://github.com/llvm/llvm-project/issues/127873>`__",""
"`P0472R3 <https://wg21.link/P0472R3>`__","Put ``std::monostate`` in ``<utility>``","2025-02 (Hagenberg)","|Complete|","21","`#127874 <https://github.com/llvm/llvm-project/issues/127874>`__",""
"`P3349R1 <https://wg21.link/P3349R1>`__","Converting contiguous iterators to pointers","2025-02 (Hagenberg)","","","`#127875 <https://github.com/llvm/llvm-project/issues/127875>`__",""
"`P3372R3 <https://wg21.link/P3372R3>`__","constexpr containers and adaptors","2025-02 (Hagenberg)","|In Progress|","","`#127876 <https://github.com/llvm/llvm-project/issues/127876>`__",""
@@ -190,7 +190,7 @@
"`P3986R1 <https://wg21.link/P3986R1>`__","A Wording Strategy for Inlinable Receivers","2026-03 (Croydon)","","","`#189598 <https://github.com/llvm/llvm-project/issues/189598>`__",""
"`P3059R2 <https://wg21.link/P3059R2>`__","Making user-defined constructors of view iterators/sentinels private","2026-03 (Croydon)","|Complete|","24","`#189599 <https://github.com/llvm/llvm-project/issues/189599>`__",""
"`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
-"`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
+"`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","|Complete|","24","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
"`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""
"`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","|Complete|","24","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
"`P3978R3 <https://wg21.link/P3978R3>`__","``constant_wrapper`` should unwrap on call and subscript","2026-03 (Croydon)","|Complete|","23","`#189605 <https://github.com/llvm/llvm-project/issues/189605>`__",""
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index dc1d8bb26bac4..ba92c689e423c 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -738,6 +738,7 @@ set(files
__ranges/adjacent_transform_view.h
__ranges/adjacent_view.h
__ranges/all.h
+ __ranges/as_input_view.h
__ranges/as_rvalue_view.h
__ranges/chunk_by_view.h
__ranges/common_view.h
diff --git a/libcxx/include/__ranges/as_input_view.h b/libcxx/include/__ranges/as_input_view.h
new file mode 100644
index 0000000000000..aea76f0f15a9f
--- /dev/null
+++ b/libcxx/include/__ranges/as_input_view.h
@@ -0,0 +1,224 @@
+// -*- 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_AS_INPUT_VIEW_H
+#define _LIBCPP___RANGES_AS_INPUT_VIEW_H
+
+#include <__concepts/constructible.h>
+#include <__concepts/convertible_to.h>
+#include <__config>
+#include <__iterator/concepts.h>
+#include <__iterator/iter_move.h>
+#include <__iterator/iter_swap.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 <__type_traits/maybe_const.h>
+#include <__utility/forward.h>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+
+namespace ranges {
+
+// [range.as.input.view]
+
+template <input_range _View>
+ requires view<_View>
+class as_input_view : public view_interface<as_input_view<_View>> {
+ _View __base_ = _View();
+
+ template <bool _Const>
+ class __iterator;
+
+public:
+ _LIBCPP_HIDE_FROM_ABI as_input_view()
+ requires default_initializable<_View>
+ = default;
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit as_input_view(_View __base) : __base_(std::move(__base)) {}
+
+ _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+ requires copy_constructible<_View>
+ {
+ return __base_;
+ }
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin()
+ requires(!__simple_view<_View>)
+ {
+ return __iterator<false>{ranges::begin(__base_)};
+ }
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+ requires range<const _View>
+ {
+ return __iterator<true>{ranges::begin(__base_)};
+ }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+ requires(!__simple_view<_View>)
+ {
+ return ranges::end(__base_);
+ }
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+ requires range<const _View>
+ {
+ return ranges::end(__base_);
+ }
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+ requires sized_range<_View>
+ {
+ return ranges::size(__base_);
+ }
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+ requires sized_range<const _View>
+ {
+ return ranges::size(__base_);
+ }
+
+ // TODO: Implement when P2846R6 is available.
+ // constexpr auto reserve_hint()
+ // requires approximately_sized_range<_View>
+ // {
+ // return ranges::reserve_hint(__base_);
+ // }
+ // constexpr auto reserve_hint() const
+ // requires approximately_sized_range<const _View>
+ // {
+ // return ranges::reserve_hint(__base_);
+ // }
+};
+
+template <class _Range>
+as_input_view(_Range&&) -> as_input_view<views::all_t<_Range>>;
+
+// [range.to.input.iterator]
+
+template <input_range _View>
+ requires view<_View>
+template <bool _Const>
+class as_input_view<_View>::__iterator {
+ using _Base _LIBCPP_NODEBUG = __maybe_const<_Const, _View>;
+
+ iterator_t<_Base> __current_ = iterator_t<_Base>();
+
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(iterator_t<_Base> __current) : __current_(std::move(__current)) {}
+
+ friend class as_input_view<_View>;
+
+public:
+ using difference_type = range_difference_t<_Base>;
+ using value_type = range_value_t<_Base>;
+ using iterator_concept = input_iterator_tag;
+
+ _LIBCPP_HIDE_FROM_ABI __iterator()
+ requires default_initializable<iterator_t<_Base>>
+ = default;
+
+ _LIBCPP_HIDE_FROM_ABI __iterator(__iterator&&) = default;
+ _LIBCPP_HIDE_FROM_ABI __iterator& operator=(__iterator&&) = default;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
+ requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
+ : __current_(std::move(__i.__current_)) {}
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() && { return std::move(__current_); }
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept { return __current_; }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return *__current_; }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() {
+ ++__current_;
+ return *this;
+ }
+ _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { ++*this; }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const sentinel_t<_Base>& __y) {
+ return __x.__current_ == __y;
+ }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+ operator-(const sentinel_t<_Base>& __y, const __iterator& __x)
+ requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
+ {
+ return __y - __x.__current_;
+ }
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+ operator-(const __iterator& __x, const sentinel_t<_Base>& __y)
+ requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
+ {
+ return __x.__current_ - __y;
+ }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_rvalue_reference_t<_Base> _LIBCPP_HIDE_FROM_ABI
+ iter_move(const __iterator& __i) noexcept(noexcept(ranges::iter_move(__i.__current_))) {
+ return ranges::iter_move(__i.__current_);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr void
+ iter_swap(const __iterator& __x,
+ const __iterator& __y) noexcept(noexcept(ranges::iter_swap(__x.__current_, __y.__current_)))
+ requires indirectly_swappable<iterator_t<_Base>>
+ {
+ ranges::iter_swap(__x.__current_, __y.__current_);
+ }
+};
+
+template <class _View>
+constexpr bool enable_borrowed_range<as_input_view<_View>> = enable_borrowed_range<_View>;
+
+namespace views {
+namespace __as_input_view {
+
+struct __fn : __range_adaptor_closure<__fn> {
+ template <input_range _Range>
+ requires(!common_range<_Range> && !forward_range<_Range>)
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto
+ operator()(_Range&& __range) noexcept(noexcept(views::all(std::forward<_Range>(__range))))
+ -> decltype(/*--------------------------*/ views::all(std::forward<_Range>(__range))) {
+ return /*---------------------------------*/ views::all(std::forward<_Range>(__range));
+ }
+
+ template <class _Range>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto
+ operator()(_Range&& __range) noexcept(noexcept(as_input_view(std::forward<_Range>(__range))))
+ -> decltype(/*--------------------------*/ as_input_view(std::forward<_Range>(__range))) {
+ return /*---------------------------------*/ as_input_view(std::forward<_Range>(__range));
+ }
+};
+
+} // namespace __as_input_view
+
+inline namespace __cpo {
+inline constexpr auto as_input = __as_input_view::__fn{};
+} // namespace __cpo
+} // namespace views
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___RANGES_AS_INPUT_VIEW_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index f56d48784737b..2639acaee1f0b 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2011,6 +2011,9 @@ module std {
header "__ranges/to.h"
export std.functional.bind_back
}
+ module as_input_view {
+ header "__ranges/as_input_view.h"
+ }
module transform_view {
header "__ranges/transform_view.h"
export std.functional.bind_back
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index b764769095a86..4bce7ef97ed71 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -428,6 +428,17 @@ namespace std::ranges {
enable_borrowed_range<V>; // C++23
namespace views { inline constexpr unspecified stride = unspecified; } // C++23
+
+ // [range.to.input], to input view
+ template<input_range V>
+ requires view<V>
+ class as_input_view; // C++26
+
+ template<class V>
+ constexpr bool enable_borrowed_range<as_input_view<V>> =
+ enable_borrowed_range<V>; // C++26
+
+ namespace views { inline constexpr unspecified as_input = unspecified; } // C++26
}
namespace std {
@@ -525,6 +536,7 @@ namespace std {
# endif
# if _LIBCPP_STD_VER >= 26
+# include <__ranges/as_input_view.h>
# include <__ranges/concat_view.h>
# endif
diff --git a/libcxx/include/version b/libcxx/include/version
index 5d0b96a1fe230..306d0436534f9 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -207,6 +207,7 @@ __cpp_lib_ranges 202406L <algorithm> <fun
<memory> <ranges>
202110L // C++20
__cpp_lib_ranges_as_const 202207L <ranges>
+__cpp_lib_ranges_as_input 202502L <ranges>
__cpp_lib_ranges_as_rvalue 202207L <ranges>
__cpp_lib_ranges_chunk 202202L <ranges>
__cpp_lib_ranges_chunk_by 202202L <ranges>
@@ -619,6 +620,7 @@ __cpp_lib_void_t 201411L <type_traits>
# undef __cpp_lib_out_ptr
# define __cpp_lib_out_ptr 202311L
// # define __cpp_lib_philox_engine 202406L
+# define __cpp_lib_ranges_as_input 202502L
# define __cpp_lib_ranges_concat 202403L
# define __cpp_lib_ranges_indices 202506L
# define __cpp_lib_ratio 202306L
diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc
index 876942bdffbf5..e4ca590edce8f 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -372,6 +372,16 @@ export namespace std {
using std::ranges::views::enumerate;
}
#endif // _LIBCPP_STD_VER >= 23
+
+#if _LIBCPP_STD_VER >= 26
+ // [range.to.input], to input view
+ using std::ranges::as_input_view;
+
+ namespace views {
+ using std::ranges::views::as_input;
+ }
+#endif
+
} // namespace ranges
namespace views = ranges::views;
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.as.input/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.as.input/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..d3202b6d53e20
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.as.input/nodiscard.verify.cpp
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <ranges>
+
+// template<input_range V>
+// requires view<V>
+// class as_input_view : public view_interface<as_input_view<V>>
+
+// Check that functions are marked [[nodiscard]]
+
+#include <concepts>
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "test_iterators.h"
+
+void test() {
+ std::vector<int> commonRange;
+ static_assert(std::ranges::input_range<decltype(commonRange)>);
+ static_assert(std::ranges::common_range<decltype(commonRange)>);
+ static_assert(std::ranges::forward_range<decltype(commonRange)>);
+
+ { // [commonRange.to.input.view]
+
+ std::ranges::as_input_view v{commonRange};
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ v.begin();
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::as_const(v).begin();
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ v.end();
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::as_const(v).end();
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ v.size();
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::as_const(v).size();
+ }
+
+ struct NonCommonRange {
+ auto begin() { return cpp17_input_iterator<int*>{nullptr}; };
+ auto end() { return sized_sentinel<cpp17_input_iterator<int*>>{begin()}; };
+ } nonCommonRange;
+ static_assert(std::ranges::input_range<NonCommonRange>);
+ static_assert(!std::ranges::common_range<NonCommonRange>);
+ static_assert(!std::ranges::forward_range<NonCommonRange>);
+
+ { // [commonRange.to.input.iterator]
+
+ std::ranges::as_input_view v{nonCommonRange};
+
+ auto it = v.begin();
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::move(it).base();
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::as_const(it).base();
+
+ auto st = nonCommonRange.end();
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ st - it;
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ it - st;
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ iter_move(it);
+ }
+
+ { // [commonRange.to.input.overview]
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::views::as_input(nonCommonRange);
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::views::as_input(commonRange);
+ }
+}
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
index 670b0e664721a..b3063599f2bad 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
@@ -32,6 +32,10 @@
# error "__cpp_lib_ranges_as_const should not be defined before c++23"
# endif
+# ifdef __cpp_lib_ranges_as_input
+# error "__cpp_lib_ranges_as_input should not be defined before c++26"
+# endif
+
# ifdef __cpp_lib_ranges_as_rvalue
# error "__cpp_lib_ranges_as_rvalue should not be defined before c++23"
# endif
@@ -94,6 +98,10 @@
# error "__cpp_lib_ranges_as_const should not be defined before c++23"
# endif
+# ifdef __cpp_lib_ranges_as_input
+# error "__cpp_lib_ranges_as_input should not be defined before c++26"
+# endif
+
# ifdef __cpp_lib_ranges_as_rvalue
# error "__cpp_lib_ranges_as_rvalue should not be defined before c++23"
# endif
@@ -156,6 +164,10 @@
# error "__cpp_lib_ranges_as_const should not be defined before c++23"
# endif
+# ifdef __cpp_lib_ranges_as_input
+# error "__cpp_lib_ranges_as_input should not be defined before c++26"
+# endif
+
# ifdef __cpp_lib_ranges_as_rvalue
# error "__cpp_lib_ranges_as_rvalue should not be defined before c++23"
# endif
@@ -221,6 +233,10 @@
# error "__cpp_lib_ranges_as_const should not be defined before c++23"
# endif
+# ifdef __cpp_lib_ranges_as_input
+# error "__cpp_lib_ranges_as_input should not be defined before c++26"
+# endif
+
# ifdef __cpp_lib_ranges_as_rvalue
# error "__cpp_lib_ranges_as_rvalue should not be defined before c++23"
# endif
@@ -295,6 +311,10 @@
# endif
# endif
+# ifdef __cpp_lib_ranges_as_input
+# error "__cpp_lib_ranges_as_input should not be defined before c++26"
+# endif
+
# ifndef __cpp_lib_ranges_as_rvalue
# error "__cpp_lib_ranges_as_rvalue should be defined in c++23"
# endif
@@ -420,6 +440,13 @@
# endif
# endif
+# ifndef __cpp_lib_ranges_as_input
+# error "__cpp_lib_ranges_as_input should be defined in c++26"
+# endif
+# if __cpp_lib_ranges_as_input != 202502L
+# error "__cpp_lib_ranges_as_input should have the value 202502L in c++26"
+# endif
+
# ifndef __cpp_lib_ranges_as_rvalue
# error "__cpp_lib_ranges_as_rvalue should be defined in c++26"
# endif
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index 9143d715ff7d7..cf9ff17c58da2 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -660,6 +660,10 @@
# error "__cpp_lib_ranges_as_const should not be defined before c++23"
# endif
+# ifdef __cpp_lib_ranges_as_input
+# error "__cpp_lib_ranges_as_input should not be defined before c++26"
+# endif
+
# ifdef __cpp_lib_ranges_as_rvalue
# error "__cpp_lib_ranges_as_rvalue should not be defined before c++23"
# endif
@@ -1636,6 +1640,10 @@
# error "__cpp_lib_ranges_as_const should not be defined before c++23"
# endif
+# ifdef __cpp_lib_ranges_as_input
+# error "__cpp_lib_ranges_as_input should not be defined before c++26"
+# endif
+
# ifdef __cpp_lib_ranges_as_rvalue
# error "__cpp_lib_ranges_as_rvalue should not be defined before c++23"
# endif
@@ -2777,6 +2785,10 @@
# error "__cpp_lib_ranges_as_const should not be defined before c++23"
# endif
+# ifdef __cpp_lib_ranges_as_input
+# error "__cpp_lib_ranges_as_input should not be defined before c++26"
+# endif
+
# ifdef __cpp_lib_ranges_as_rvalue
# error "__cpp_lib_ranges_as_rvalue should not be defined before c++23"
# endif
@@ -4185,6 +4197,10 @@
# error "__cpp_lib_ranges_as_const should not be defined before c++23"
# endif
+# ifdef __cpp_lib_ranges_as_input
+# error "__cpp_lib_ranges_as_input should not be defined before c++26"
+# endif
+
# ifdef __cpp_lib_ranges_as_rvalue
# error "__cpp_lib_ranges_as_rvalue should not be defined before c++23"
# endif
@@ -5767,6 +5783,10 @@
# endif
# endif
+# ifdef __cpp_lib_ranges_as_input
+# error "__cpp_lib_ranges_as_input should not be defined before c++26"
+# endif
+
# ifndef __cpp_lib_ranges_as_rvalue
# error "__cpp_lib_ranges_as_rvalue should be defined in c++23"
# endif
@@ -7706,6 +7726,13 @@
# endif
# endif
+# ifndef __cpp_lib_ranges_as_input
+# error "__cpp_lib_ranges_as_input should be defined in c++26"
+# endif
+# if __cpp_lib_ranges_as_input != 202502L
+# error "__cpp_lib_ranges_as_input should have the value 202502L in c++26"
+# endif
+
# ifndef __cpp_lib_ranges_as_rvalue
# error "__cpp_lib_ranges_as_rvalue should be defined in c++26"
# endif
diff --git a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
index 71a0b6ebb804c..6f9f2f52ea647 100644
--- a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
+++ b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
@@ -137,6 +137,6 @@ static_assert(test(std::views::zip, a, a));
#if TEST_STD_VER >= 26
// static_assert(test(std::views::cache_latest, a));
+static_assert(test(std::views::as_input, a));
static_assert(test(std::views::concat, a, a));
-// static_assert(test(std::views::to_input, a));
#endif
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.input/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.input/adaptor.pass.cpp
new file mode 100644
index 0000000000000..da36b4d862826
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.input/adaptor.pass.cpp
@@ -0,0 +1,278 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <ranges>
+
+// template<input_range V>
+// requires view<V>
+// class as_input_view : public view_interface<as_input_view<V>>
+
+// [range.to.input.overview]
+
+#include <cassert>
+#include <concepts>
+#include <functional>
+#include <ranges>
+#include <vector>
+
+#include "test_iterators.h"
+#include "test_range.h"
+
+struct NonView {};
+
+static_assert(std::default_initializable<NonView>);
+
+struct DefaultInitializableView : std::ranges::view_base {
+ int i_;
+
+ int* begin();
+ int* end();
+};
+
+static_assert(std::default_initializable<DefaultInitializableView>);
+static_assert(std::ranges::common_range<DefaultInitializableView>);
+static_assert(std::ranges::input_range<DefaultInitializableView>);
+
+struct CommonView : std::ranges::view_base {
+ int i_;
+
+ constexpr forward_iterator<int*> begin() { return forward_iterator<int*>(&i_); }
+ constexpr forward_iterator<int*> end() { return begin(); }
+};
+
+static_assert(std::ranges::common_range<CommonView>);
+static_assert(std::ranges::forward_range<CommonView>);
+static_assert(std::ranges::input_range<CommonView>);
+
+struct NonCommonView : std::ranges::view_base {
+ int i_;
+
+ constexpr forward_iterator<int*> begin() { return forward_iterator<int*>(&i_); }
+ constexpr sentinel_wrapper<forward_iterator<int*>> end() { return sentinel_wrapper<forward_iterator<int*>>(begin()); }
+};
+
+static_assert(!std::ranges::common_range<NonCommonView>);
+static_assert(std::ranges::forward_range<NonCommonView>);
+static_assert(std::ranges::input_range<NonCommonView>);
+static_assert(
+ std::derived_from< typename std::iterator_traits<std::ranges::iterator_t<NonCommonView>>::iterator_category,
+ std::input_iterator_tag>);
+
+// Check that the `as_input` adaptor can be used with a view.
+
+static_assert(!std::is_invocable_v<decltype(std::views::as_input)>);
+static_assert(!std::is_invocable_v<decltype(std::views::as_input), NonView>);
+static_assert(std::is_invocable_v<decltype(std::views::as_input), DefaultInitializableView>);
+static_assert(std::is_invocable_v<decltype(std::views::as_input), CommonView>);
+static_assert(std::is_invocable_v<decltype(std::views::as_input), NonCommonView>);
+
+static_assert(!CanBePiped<NonView, decltype(std::views::as_input)>);
+static_assert(CanBePiped<DefaultInitializableView&, decltype(std::views::as_input)>);
+static_assert(CanBePiped<CommonView&, decltype(std::views::as_input)>);
+static_assert(CanBePiped<NonCommonView&, decltype(std::views::as_input)>);
+
+constexpr bool test() {
+ // Sameness
+ {
+ static_assert(std::is_same_v<decltype(std::views::as_input), decltype(std::ranges::views::as_input)>);
+ assert(std::addressof(std::views::as_input) == std::addressof(std::ranges::views::as_input));
+ }
+
+ { // view | views::as_input
+
+ DefaultInitializableView view{{}, 94};
+ std::same_as<std::ranges::as_input_view<DefaultInitializableView>> decltype(auto) v =
+ view | std::views::as_input | std::views::as_input;
+ assert(v.base().i_ == 94);
+
+ static_assert(!std::ranges::common_range<decltype(v)>);
+ static_assert(!std::ranges::forward_range<decltype(v)>);
+ static_assert(std::ranges::input_range<decltype(v)>);
+ }
+
+ { // view | views::as_input
+ {
+ DefaultInitializableView view{{}, 94};
+ std::same_as<std::ranges::as_input_view<DefaultInitializableView>> decltype(auto) v = view | std::views::as_input;
+ assert(v.base().i_ == 94);
+
+ static_assert(!std::ranges::common_range<decltype(v)>);
+ static_assert(!std::ranges::forward_range<decltype(v)>);
+ static_assert(std::ranges::input_range<decltype(v)>);
+ }
+ {
+ CommonView view{{}, 94};
+ std::same_as<std::ranges::as_input_view<CommonView>> decltype(auto) v = view | std::views::as_input;
+ assert(v.base().i_ == 94);
+
+ static_assert(!std::ranges::common_range<decltype(v)>);
+ static_assert(!std::ranges::forward_range<decltype(v)>);
+ static_assert(std::ranges::input_range<decltype(v)>);
+ }
+ {
+ NonCommonView view{{}, 94};
+ std::same_as<std::ranges::as_input_view<NonCommonView>> decltype(auto) v = view | std::views::as_input;
+ assert(v.base().i_ == 94);
+
+ static_assert(!std::ranges::common_range<decltype(v)>);
+ static_assert(!std::ranges::forward_range<decltype(v)>);
+ static_assert(std::ranges::input_range<decltype(v)>);
+ }
+ }
+
+ { // adaptor | views::as_input
+ {
+ DefaultInitializableView view{{}, 94};
+ const auto partial = std::views::transform(std::identity{}) | std::views::as_input;
+ std::same_as<std::ranges::as_input_view<
+ std::ranges::transform_view<DefaultInitializableView, std::identity>>> decltype(auto) v = partial(view);
+ assert(v.base().base().i_ == 94);
+
+ static_assert(!std::ranges::common_range<decltype(v)>);
+ static_assert(!std::ranges::forward_range<decltype(v)>);
+ static_assert(std::ranges::input_range<decltype(v)>);
+ }
+ {
+ CommonView view{{}, 94};
+ const auto partial = std::views::transform(std::identity{}) | std::views::as_input;
+ std::same_as<std::ranges::as_input_view< std::ranges::transform_view<CommonView, std::identity>>> decltype(auto)
+ v = partial(view);
+ assert(v.base().base().i_ == 94);
+
+ static_assert(!std::ranges::common_range<decltype(v)>);
+ static_assert(!std::ranges::forward_range<decltype(v)>);
+ static_assert(std::ranges::input_range<decltype(v)>);
+ }
+ {
+ NonCommonView view{{}, 94};
+ const auto partial = std::views::transform(std::identity{}) | std::views::as_input;
+ std::same_as<
+ std::ranges::as_input_view< std::ranges::transform_view<NonCommonView, std::identity>>> decltype(auto) v =
+ partial(view);
+ assert(v.base().base().i_ == 94);
+
+ static_assert(!std::ranges::common_range<decltype(v)>);
+ static_assert(!std::ranges::forward_range<decltype(v)>);
+ static_assert(std::ranges::input_range<decltype(v)>);
+ }
+ }
+
+ { // views::as_input | adaptor
+ {
+ DefaultInitializableView view{{}, 94};
+ const auto partial = std::views::as_input | std::views::transform(std::identity{});
+ std::same_as<std::ranges::transform_view<std::ranges::as_input_view<DefaultInitializableView>,
+ std::identity>> decltype(auto) v = partial(view);
+ assert(v.base().base().i_ == 94);
+
+ static_assert(std::ranges::input_range<decltype(v)>);
+ static_assert(!std::ranges::forward_range<decltype(v)>);
+ static_assert(!std::ranges::common_range<decltype(v)>);
+ }
+ {
+ CommonView view{{}, 94};
+ const auto partial = std::views::as_input | std::views::transform(std::identity{});
+ std::same_as<std::ranges::transform_view<std::ranges::as_input_view<CommonView>, std::identity>> decltype(auto)
+ v = partial(view);
+ assert(v.base().base().i_ == 94);
+
+ static_assert(std::ranges::input_range<decltype(v)>);
+ static_assert(!std::ranges::forward_range<decltype(v)>);
+ static_assert(!std::ranges::common_range<decltype(v)>);
+ }
+ {
+ NonCommonView view{{}, 94};
+ const auto partial = std::views::as_input | std::views::transform(std::identity{});
+ std::same_as<
+ std::ranges::transform_view<std::ranges::as_input_view<NonCommonView>, std::identity>> decltype(auto) v =
+ partial(view);
+ assert(v.base().base().i_ == 94);
+
+ static_assert(std::ranges::input_range<decltype(v)>);
+ static_assert(!std::ranges::forward_range<decltype(v)>);
+ static_assert(!std::ranges::common_range<decltype(v)>);
+ }
+ }
+
+ { // views::as_input | views::all
+ {
+ DefaultInitializableView view{{}, 94};
+ std::same_as<std::ranges::as_input_view<DefaultInitializableView>> decltype(auto) v =
+ std::views::all(view) | std::views::as_input;
+ assert(v.base().i_ == 94);
+
+ static_assert(std::ranges::input_range<decltype(v)>);
+ static_assert(!std::ranges::forward_range<decltype(v)>);
+ static_assert(!std::ranges::common_range<decltype(v)>);
+ }
+ {
+ CommonView view{{}, 94};
+ std::same_as<std::ranges::as_input_view<CommonView>> decltype(auto) v =
+ std::views::all(view) | std::views::as_input;
+ assert(v.base().i_ == 94);
+
+ static_assert(std::ranges::input_range<decltype(v)>);
+ static_assert(!std::ranges::forward_range<decltype(v)>);
+ static_assert(!std::ranges::common_range<decltype(v)>);
+ }
+ {
+ NonCommonView view{{}, 94};
+ std::same_as<std::ranges::as_input_view<NonCommonView>> decltype(auto) v =
+ std::views::all(view) | std::views::as_input;
+ assert(v.base().i_ == 94);
+
+ static_assert(std::ranges::input_range<decltype(v)>);
+ static_assert(!std::ranges::forward_range<decltype(v)>);
+ static_assert(!std::ranges::common_range<decltype(v)>);
+ }
+ }
+
+ { // views::as_input | views::all_t
+ {
+ DefaultInitializableView view{{}, 94};
+ std::same_as<std::ranges::as_input_view<DefaultInitializableView>> decltype(auto) v =
+ std::views::all_t<DefaultInitializableView>(view) | std::views::as_input;
+ assert(v.base().i_ == 94);
+
+ static_assert(std::ranges::input_range<decltype(v)>);
+ static_assert(!std::ranges::forward_range<decltype(v)>);
+ static_assert(!std::ranges::common_range<decltype(v)>);
+ }
+ {
+ CommonView view{{}, 94};
+ std::same_as<std::ranges::as_input_view<CommonView>> decltype(auto) v =
+ std::views::all_t<CommonView>(view) | std::views::as_input;
+ assert(v.base().i_ == 94);
+
+ static_assert(std::ranges::input_range<decltype(v)>);
+ static_assert(!std::ranges::forward_range<decltype(v)>);
+ static_assert(!std::ranges::common_range<decltype(v)>);
+ }
+ {
+ NonCommonView view{{}, 94};
+ std::same_as<std::ranges::as_input_view<NonCommonView>> decltype(auto) v =
+ std::views::all_t<NonCommonView>(view) | std::views::as_input;
+ assert(v.base().i_ == 94);
+
+ static_assert(std::ranges::input_range<decltype(v)>);
+ static_assert(!std::ranges::forward_range<decltype(v)>);
+ static_assert(!std::ranges::common_range<decltype(v)>);
+ }
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.input/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.input/base.pass.cpp
new file mode 100644
index 0000000000000..205a9e96e90d3
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.input/base.pass.cpp
@@ -0,0 +1,86 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <ranges>
+
+// template<input_range V>
+// requires view<V>
+// class as_input_view : public view_interface<as_input_view<V>>
+
+// constexpr V base() const & requires copy_constructible<V> { return base_; }
+// constexpr V base() && { return std::move(base_); }
+
+#include <cassert>
+#include <concepts>
+#include <ranges>
+#include <utility>
+
+#include "MoveOnly.h"
+
+struct SimpleView : std::ranges::view_base {
+ int i_;
+
+ int* begin() const;
+ int* end() const;
+};
+
+struct MoveOnlyView : public SimpleView {
+ MoveOnly m_;
+};
+
+template <class T>
+concept HasBase = requires(T&& t) { std::forward<T>(t).base(); };
+
+static_assert(HasBase<std::ranges::as_input_view<SimpleView> const&>);
+static_assert(HasBase<std::ranges::as_input_view<SimpleView>&&>);
+
+static_assert(!HasBase<std::ranges::as_input_view<MoveOnlyView> const&>);
+static_assert(HasBase<std::ranges::as_input_view<MoveOnlyView>&&>);
+
+constexpr bool test() {
+ { // &
+ std::ranges::as_input_view<SimpleView> view(SimpleView{{}, 94});
+ std::same_as<SimpleView> decltype(auto) v = view.base();
+ assert(v.i_ == 94);
+ }
+
+ { // const &
+ const std::ranges::as_input_view<SimpleView> view(SimpleView{{}, 94});
+ std::same_as<SimpleView> decltype(auto) v = view.base();
+ assert(v.i_ == 94);
+ }
+
+ { // &&
+ std::ranges::as_input_view<SimpleView> view(SimpleView{{}, 94});
+ std::same_as<SimpleView> decltype(auto) v = std::move(view).base();
+ assert(v.i_ == 94);
+ }
+
+ { // const &&
+ const std::ranges::as_input_view<SimpleView> view(SimpleView{{}, 94});
+ std::same_as<SimpleView> decltype(auto) v = std::move(view).base();
+ assert(v.i_ == 94);
+ }
+
+ { // move only
+ std::ranges::as_input_view<MoveOnlyView> view(MoveOnlyView{{}, 94});
+ std::same_as<MoveOnlyView> decltype(auto) v = std::move(view).base();
+ assert(v.m_.get() == 94);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.input/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.input/begin.pass.cpp
new file mode 100644
index 0000000000000..39ab29d97d462
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.input/begin.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <ranges>
+
+// template<input_range V>
+// requires view<V>
+// class as_input_view : public view_interface<as_input_view<V>>
+
+// constexpr auto begin() requires (!simple-view<V>);
+// constexpr auto begin() const requires range<const V>;
+
+#include <cassert>
+#include <concepts>
+#include <ranges>
+#include <utility>
+
+constexpr bool test() { return true; }
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.input/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.input/ctad.compile.pass.cpp
new file mode 100644
index 0000000000000..883fb7acce239
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.input/ctad.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <ranges>
+
+// template<input_range V>
+// requires view<V>
+// class as_input_view : public view_interface<as_input_view<V>>
+
+// template<class R>
+// as_input_view(R&&) -> as_input_view<views::all_t<R>>;
+
+#include <concepts>
+#include <ranges>
+#include <utility>
+#include <vector>
+
+static_assert(std::same_as<decltype(std::ranges::as_input_view(std::vector<int>{})),
+ std::ranges::as_input_view<std::views::all_t<std::vector<int>>>>);
+
+static_assert(std::same_as<decltype(std::ranges::as_input_view(std::declval<std::vector<int>&>())),
+ std::ranges::as_input_view<std::views::all_t<std::vector<int>&>>>);
+
+static_assert(std::same_as<decltype(std::ranges::as_input_view(std::ranges::empty_view<int>{})),
+ std::ranges::as_input_view<std::ranges::empty_view<int>>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.input/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.input/ctor.default.pass.cpp
new file mode 100644
index 0000000000000..c2effaa04f644
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.input/ctor.default.pass.cpp
@@ -0,0 +1,83 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <ranges>
+
+// template<input_range V>
+// requires view<V>
+// class as_input_view : public view_interface<as_input_view<V>>
+
+// as_input_view() requires default_initializable<V> = default;
+
+#include <cassert>
+#include <concepts>
+#include <ranges>
+
+struct DefaultInitializableView : std::ranges::view_base {
+ int i_ = 94;
+
+ int* begin();
+ int* end();
+};
+
+static_assert(std::default_initializable<DefaultInitializableView>);
+static_assert(std::default_initializable<std::ranges::as_input_view<DefaultInitializableView>>);
+
+struct NoDefaultInitializableView : std::ranges::view_base {
+ NoDefaultInitializableView() = delete;
+
+ int* begin();
+ int* end();
+};
+
+static_assert(!std::default_initializable<NoDefaultInitializableView>);
+static_assert(!std::default_initializable<std::ranges::as_input_view<NoDefaultInitializableView>>);
+
+struct NoexceptView : std::ranges::view_base {
+ NoexceptView() noexcept;
+
+ int const* begin() const;
+ int const* end() const;
+};
+
+static_assert(noexcept(std::ranges::as_input_view<NoexceptView>()));
+
+struct NoNoexceptView : std::ranges::view_base {
+ NoNoexceptView() noexcept(false);
+
+ int const* begin() const;
+ int const* end() const;
+};
+
+static_assert(!noexcept(std::ranges::as_input_view<NoNoexceptView>()));
+
+constexpr bool test() {
+ { // value-initialized (i.e., whether T() is well-formed).
+ std::ranges::as_input_view<DefaultInitializableView> as_input_view{};
+ assert(as_input_view.base().i_ == 94);
+ }
+ { // direct-list-initialized from an empty initializer list (i.e., whether T{} is well-formed).
+ std::ranges::as_input_view<DefaultInitializableView> as_input_view = {};
+ assert(as_input_view.base().i_ == 94);
+ }
+ { // default-initialized (i.e., whether T t; is well-formed).
+ std::ranges::as_input_view<DefaultInitializableView> as_input_view;
+ assert(as_input_view.base().i_ == 94);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.input/ctor.view.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.input/ctor.view.pass.cpp
new file mode 100644
index 0000000000000..1ec4fa3582018
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.input/ctor.view.pass.cpp
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <ranges>
+
+// template<input_range V>
+// requires view<V>
+// class as_input_view : public view_interface<as_input_view<V>>
+
+// constexpr explicit as_input_view(V base);
+
+constexpr bool test() { return true; }
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.input/enable_borrowed_range.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.input/enable_borrowed_range.compile.pass.cpp
new file mode 100644
index 0000000000000..4f23d907b9686
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.input/enable_borrowed_range.compile.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <ranges>
+
+// template<input_range V>
+// requires view<V>
+// class as_input_view : public view_interface<as_input_view<V>>
+
+// template<class V>
+// constexpr bool enable_borrowed_range<as_input_view<V>> =
+// enable_borrowed_range<V>;
+
+#include <ranges>
+
+struct BorrowedView : std::ranges::view_base {
+ int* begin();
+ int* end();
+};
+
+template <>
+inline constexpr bool std::ranges::enable_borrowed_range<BorrowedView> = true;
+
+static_assert(std::ranges::borrowed_range<std::ranges::as_input_view<BorrowedView>>);
+
+struct NonBorrowedView : std::ranges::view_base {
+ int* begin();
+ int* end();
+};
+
+static_assert(!std::ranges::borrowed_range<std::ranges::as_input_view<NonBorrowedView>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.input/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.input/end.pass.cpp
new file mode 100644
index 0000000000000..89d5a9280a775
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.input/end.pass.cpp
@@ -0,0 +1,31 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <ranges>
+// template<input_range V>
+// requires view<V>
+// class as_input_view : public view_interface<as_input_view<V>>
+
+// constexpr auto end() requires (!simple-view<V>);
+// constexpr auto end() const requires range<const V>;
+
+#include <cassert>
+#include <concepts>
+#include <ranges>
+#include <utility>
+
+constexpr bool test() { return true; }
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.input/general.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.input/general.pass.cpp
new file mode 100644
index 0000000000000..6cb939e1d0c29
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.input/general.pass.cpp
@@ -0,0 +1,60 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <ranges>
+
+// class as_input_view
+
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <ranges>
+
+// template<input_range V>
+// requires view<V>
+// class as_input_view : public view_interface<as_input_view<V>>
+
+// Functional tests of std::ranges::as_input_view.
+
+#include <algorithm>
+#include <cassert>
+#include <ranges>
+#include <string>
+#include <vector>
+
+constexpr bool test() {
+ std::vector<std::string> vec{"Hello", ",", " ", "World", "!"};
+ std::string expectedStr = "Hello, World!";
+
+ {
+ auto view = vec | std::views::join;
+ assert(std::ranges::equal(view, expectedStr));
+ }
+ { // Test as_input_view with a vector of strings.
+ auto view = vec | std::views::as_input | std::views::join;
+ assert(std::ranges::equal(view, expectedStr));
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/base.pass.cpp
new file mode 100644
index 0000000000000..4d017ad69854d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/base.pass.cpp
@@ -0,0 +1,167 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <ranges>
+
+// template<input_range V>
+// requires view<V>
+// template<bool Const>
+// class as_input_view<V>::iterator
+
+// constexpr iterator_t<Base> base() &&;
+// constexpr const iterator_t<Base>& base() const & noexcept;
+
+#include <cassert>
+#include <concepts>
+#include <ranges>
+#include <utility>
+
+#include "test_iterators.h"
+
+class CopyConstructibleView : public std::ranges::view_interface<CopyConstructibleView> {
+ int* data_;
+ std::size_t size_;
+
+public:
+ constexpr CopyConstructibleView(int* data, std::size_t size) : data_(data), size_(size) {}
+
+ constexpr CopyConstructibleView(const CopyConstructibleView&) = default;
+ constexpr CopyConstructibleView& operator=(const CopyConstructibleView&) = default;
+
+ constexpr CopyConstructibleView(CopyConstructibleView&&) = default;
+ constexpr CopyConstructibleView& operator=(CopyConstructibleView&&) = default;
+
+ constexpr int* begin() const { return data_; }
+ constexpr int* end() const { return data_ + size_; }
+
+ constexpr std::size_t size() const { return size_; }
+};
+
+class NonCopyConstructibleView : public std::ranges::view_interface<NonCopyConstructibleView> {
+ int* data_;
+ std::size_t size_;
+
+public:
+ constexpr NonCopyConstructibleView(int* data, std::size_t size) : data_(data), size_(size) {}
+
+ constexpr NonCopyConstructibleView(const NonCopyConstructibleView&) = delete;
+ constexpr NonCopyConstructibleView& operator=(const NonCopyConstructibleView&) = delete;
+
+ constexpr NonCopyConstructibleView(NonCopyConstructibleView&&) = default;
+ constexpr NonCopyConstructibleView& operator=(NonCopyConstructibleView&&) = default;
+
+ constexpr int* begin() const { return data_; }
+ constexpr int* end() const { return data_ + size_; }
+ constexpr std::size_t size() const { return size_; }
+};
+
+constexpr bool test() {
+ { // base() &&
+ int arr[] = {94};
+ CopyConstructibleView range{arr, 1};
+
+ std::ranges::as_input_view<CopyConstructibleView> iv{range};
+
+ auto it = iv.begin();
+
+ using IteratorT = std::ranges::iterator_t<CopyConstructibleView>;
+
+ std::same_as<IteratorT> decltype(auto) base_it = std::move(it).base();
+ static_assert(!noexcept(std::move(it).base()));
+
+ assert(base(base_it) == arr);
+ assert(*base_it == 94);
+ }
+
+ { // base() const & noexcept
+ {
+ int arr[] = {94};
+ CopyConstructibleView range{arr, 1};
+
+ std::ranges::as_input_view<CopyConstructibleView> iv{range};
+
+ auto it = iv.begin();
+
+ using IteratorT = std::ranges::iterator_t<CopyConstructibleView>;
+
+ { // &
+ std::same_as<const Iterator&> decltype(auto) base_it = it.base();
+ static_assert(noexcept(it.base()));
+
+ assert(base(base_it) == arr);
+ assert(*base_it == 94);
+ }
+ { // const &
+ std::same_as<const IteratorT&> decltype(auto) base_it = std::as_const(it).base();
+ static_assert(noexcept(it.base()));
+
+ assert(base(base_it) == arr);
+ assert(*base_it == 94);
+ }
+ { // && - selects base() &&
+ }
+ { // const &&
+ std::same_as<const IteratorT&> decltype(auto) base_it = std::move(std::as_const(it)).base();
+ static_assert(noexcept(it.base()));
+
+ assert(base(base_it) == arr);
+ assert(*base_it == 94);
+ }
+ }
+ {
+ int arr[] = {82};
+ NonCopyConstructibleView range{arr, 1};
+
+ const std::ranges::as_input_view<NonCopyConstructibleView> iv{std::move(range)};
+
+ auto it = iv.begin();
+
+ using IteratorT = std::ranges::iterator_t<NonCopyConstructibleView>;
+
+ { // &
+ std::same_as<const IteratorT&> decltype(auto) base_it = it.base();
+ static_assert(noexcept(it.base()));
+
+ assert(base(base_it) == arr);
+ assert(*base_it == 82);
+ }
+ { // const &
+ std::same_as<const IteratorT&> decltype(auto) base_it = std::as_const(it).base();
+ static_assert(noexcept(it.base()));
+
+ assert(base(base_it) == arr);
+ assert(*base_it == 82);
+ }
+ { // &&
+ std::same_as<IteratorT> decltype(auto) base_it = std::move(it).base();
+ static_assert(noexcept(it.base()));
+
+ assert(base(base_it) == arr);
+ assert(*base_it == 82);
+ }
+ { // const&&
+ std::same_as<const IteratorT&> decltype(auto) base_it = std::move(std::as_const(it)).base();
+ static_assert(noexcept(it.base()));
+
+ assert(base(base_it) == arr);
+ assert(*base_it == 82);
+ }
+ }
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/ctor.converting.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/ctor.converting.pass.cpp
new file mode 100644
index 0000000000000..e7af7315140c1
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/ctor.converting.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <ranges>
+
+// template<input_range V>
+// requires view<V>
+// template<bool Const>
+// class as_input_view<V>::iterator
+
+// constexpr iterator(iterator<!Const> i)
+// requires Const && convertible_to<iterator_t<V>, iterator_t<Base>>;
+
+#include <cassert>
+#include <concepts>
+#include <ranges>
+#include <type_traits>
+#include <utility>
+
+#include "test_iterators.h"
+#include "test_range.h"
+
+class NonSimpleView : public std::ranges::view_interface<NonSimpleView> {
+ int* data_;
+ std::size_t size_;
+
+public:
+ constexpr NonSimpleView(int* data, std::size_t size) : data_(data), size_(size) {}
+
+ constexpr int* begin() { return data_; }
+ constexpr const int* begin() const { return data_; }
+ constexpr int* end() { return data_ + size_; }
+ constexpr const int* end() const { return data_ + size_; }
+ constexpr std::size_t size() const { return size_; }
+};
+static_assert(!simple_view<NonSimpleView>);
+
+constexpr bool test() {
+ int arr[] = {94};
+ NonSimpleView range{arr, 1};
+
+ std::ranges::as_input_view<NonSimpleView> view{range};
+
+ using IteratorT = std::ranges::iterator_t<decltype(view)>;
+ using ConstIteratorT = std::ranges::iterator_t<const decltype(view)>;
+
+ static_assert(!std::same_as<IteratorT, ConstIteratorT>);
+ static_assert(std::convertible_to<IteratorT, ConstIteratorT>);
+ static_assert(std::constructible_from<ConstIteratorT, IteratorT&&>);
+
+ IteratorT it = view.begin();
+ ConstIteratorT const_it{std::move(it)};
+
+ std::same_as<const int*> decltype(auto) base_it = std::move(const_it).base();
+ assert(base(base_it) == arr);
+ assert(*base_it == 94);
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/ctor.default.pass.cpp
new file mode 100644
index 0000000000000..6f6ae30479d95
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/ctor.default.pass.cpp
@@ -0,0 +1,83 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <ranges>
+
+// template<input_range V>
+// requires view<V>
+// template<bool Const>
+// class as_input_view<V>::iterator
+
+// iterator() requires default_initializable<iterator_t<Base>> = default;
+
+#include <cassert>
+#include <concepts>
+#include <cstdint>
+#include <ranges>
+
+#include "test_iterators.h"
+
+struct DefaultInitializableIterator {
+ int i_; // Deliberately uninitialized.
+
+ using difference_type = std::intptr_t;
+ using value_type = int;
+ using iterator_category = std::random_access_iterator_tag;
+
+ constexpr int operator*() const { return i_; }
+
+ constexpr DefaultInitializableIterator& operator++() { return *this; }
+ constexpr void operator++(int) {}
+
+ friend constexpr bool operator==(const DefaultInitializableIterator&, const DefaultInitializableIterator&) = default;
+};
+
+static_assert(std::default_initializable<DefaultInitializableIterator>);
+
+struct DefaultInitializableIteratorView : std::ranges::view_base {
+ DefaultInitializableIterator begin() const;
+ DefaultInitializableIterator end() const;
+};
+
+struct NonDefaultInitializableIteratorView : std::ranges::view_base {
+ static_assert(!std::default_initializable<cpp20_input_iterator<int*>>);
+
+ cpp20_input_iterator<int*> begin() const;
+ sentinel_wrapper<cpp20_input_iterator<int*>> end() const;
+};
+
+template <class View>
+using ToInputViewIteratorT = std::ranges::iterator_t<std::ranges::as_input_view<View>>;
+
+// Check that the as_input_view's iterator is default initializable when the underlying iterator is.
+static_assert(std::default_initializable<ToInputViewIteratorT<DefaultInitializableIteratorView>>);
+static_assert(!std::default_initializable<ToInputViewIteratorT<NonDefaultInitializableIteratorView>>);
+
+constexpr bool test() {
+ {
+ ToInputViewIteratorT<DefaultInitializableIteratorView> it;
+ assert(it.base() == DefaultInitializableIterator{});
+ assert(*it == 0); // DefaultInitializableIterator has to be initialized to have value 0.
+ }
+ {
+ ToInputViewIteratorT<DefaultInitializableIteratorView> it = {};
+ assert(it.base() == DefaultInitializableIterator{});
+ assert(*it == 0); // DefaultInitializableIterator has to be initialized 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.as.input/iterator/ctor.move.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/ctor.move.pass.cpp
new file mode 100644
index 0000000000000..fb42d67efff19
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/ctor.move.pass.cpp
@@ -0,0 +1,120 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <ranges>
+
+// template<input_range V>
+// requires view<V>
+// template<bool Const>
+// class as_input_view<V>::iterator
+
+// iterator(iterator&&) = default;
+// iterator& operator=(iterator&&) = default;
+
+#include <cassert>
+#include <concepts>
+#include <ranges>
+#include <type_traits>
+#include <utility>
+
+#include "test_iterators.h"
+
+class MoveOnlyInputIterator;
+
+class MoveOnlyInputSentinel {
+ int* ptr_ = nullptr;
+
+public:
+ constexpr MoveOnlyInputSentinel() = default;
+ constexpr explicit MoveOnlyInputSentinel(int* ptr) : ptr_(ptr) {}
+
+ constexpr int* base() const { return ptr_; }
+};
+
+class MoveOnlyInputIterator {
+ int* ptr_ = nullptr;
+
+public:
+ constexpr MoveOnlyInputIterator() = default;
+ constexpr explicit MoveOnlyInputIterator(int* ptr) : ptr_(ptr) {}
+
+ MoveOnlyInputIterator(const MoveOnlyInputIterator&) = delete;
+ MoveOnlyInputIterator& operator=(const MoveOnlyInputIterator&) = delete;
+
+ MoveOnlyInputIterator(MoveOnlyInputIterator&&) = default;
+ MoveOnlyInputIterator& operator=(MoveOnlyInputIterator&&) = default;
+
+ using difference_type = std::intptr_t;
+ using value_type = int;
+ using iterator_category = std::input_iterator_tag;
+
+ constexpr int operator*() const { return *ptr_; }
+
+ constexpr MoveOnlyInputIterator& operator++() {
+ ++ptr_;
+ return *this;
+ }
+ constexpr MoveOnlyInputIterator operator++(int) {
+ MoveOnlyInputIterator __tmp = std::move(*this);
+ ++*this;
+ return __tmp;
+ }
+
+ friend constexpr bool operator==(const MoveOnlyInputIterator& x, const MoveOnlyInputSentinel& y) {
+ return x.ptr_ == y.base();
+ }
+ friend constexpr bool operator==(const MoveOnlyInputSentinel& y, const MoveOnlyInputIterator& x) { return x == y; }
+};
+
+static_assert(std::input_iterator<MoveOnlyInputIterator>);
+
+class MoveOnlyInputView : public std::ranges::view_interface<MoveOnlyInputView> {
+ int* data_;
+ std::size_t size_;
+
+public:
+ constexpr MoveOnlyInputView(int* data, std::size_t size) : data_(data), size_(size) {}
+
+ constexpr MoveOnlyInputIterator begin() const { return MoveOnlyInputIterator(data_); }
+ constexpr MoveOnlyInputSentinel end() const { return MoveOnlyInputSentinel(data_ + size_); }
+};
+
+static_assert(std::ranges::input_range<MoveOnlyInputView>);
+
+constexpr bool test() {
+ int arr[] = {1, 2, 3};
+ std::ranges::as_input_view<MoveOnlyInputView> view{MoveOnlyInputView{arr, 3}};
+
+ using IteratorT = std::ranges::iterator_t<decltype(view)>;
+
+ static_assert(!std::copy_constructible<IteratorT>);
+ static_assert(!std::is_copy_assignable_v<IteratorT>);
+ static_assert(std::move_constructible<IteratorT>);
+ static_assert(std::is_move_assignable_v<IteratorT>);
+
+ IteratorT it1 = view.begin();
+
+ // Move constructor
+ IteratorT it2{std::move(it1)};
+ assert(*it2 == 1);
+
+ // Move assignment
+ IteratorT it3 = std::move(it2);
+ assert(*it3 == 1);
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/iter_move.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/iter_move.pass.cpp
new file mode 100644
index 0000000000000..b2660ffc927b5
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/iter_move.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <ranges>
+
+// template<input_range V>
+// requires view<V>
+// template<bool Const>
+// class as_input_view<V>::iterator
+
+// friend constexpr range_rvalue_reference_t<Base> iter_move(const iterator& i)
+// noexcept(noexcept(ranges::iter_move(i.current_)));
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+
+constexpr bool test() { return true; }
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/iter_swap.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/iter_swap.pass.cpp
new file mode 100644
index 0000000000000..27f0dada269aa
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/iter_swap.pass.cpp
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <ranges>
+
+// template<input_range V>
+// requires view<V>
+// template<bool Const>
+// class as_input_view<V>::iterator
+
+// friend constexpr void iter_swap(const iterator& x, const iterator& y)
+// noexcept(noexcept(ranges::iter_swap(x.current_, y.current_)))
+// requires indirectly_swappable<iterator_t<Base>>;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+
+constexpr bool test() { return true; }
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/op.deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/op.deref.pass.cpp
new file mode 100644
index 0000000000000..85430847dfc6f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/op.deref.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <ranges>
+
+// template<input_range V>
+// requires view<V>
+// template<bool Const>
+// class as_input_view<V>::iterator
+
+// constexpr decltype(auto) operator*() const
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+
+constexpr bool test() { return true; }
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/op.eq.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/op.eq.pass.cpp
new file mode 100644
index 0000000000000..e5ecf82101b80
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/op.eq.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <ranges>
+
+// template<input_range V>
+// requires view<V>
+// template<bool Const>
+// class as_input_view<V>::iterator
+
+// friend constexpr bool operator==(const iterator& x, const sentinel_t<Base>& y);
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+
+constexpr bool test() { return true; }
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/op.increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/op.increment.pass.cpp
new file mode 100644
index 0000000000000..4d904cfcc7a24
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/op.increment.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <ranges>
+
+// template<input_range V>
+// requires view<V>
+// template<bool Const>
+// class as_input_view<V>::iterator
+
+// constexpr iterator& operator++();
+// constexpr void operator++(int);
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+
+constexpr bool test() { return true; }
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/op.minus.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/op.minus.pass.cpp
new file mode 100644
index 0000000000000..e234dd24349a8
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.input/iterator/op.minus.pass.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <ranges>
+
+// template<input_range V>
+// requires view<V>
+// template<bool Const>
+// class as_input_view<V>::iterator
+
+// friend constexpr difference_type operator-(const sentinel_t<Base>& y, const iterator& x)
+// requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;
+// friend constexpr difference_type operator-(const iterator& x, const sentinel_t<Base>& y)
+// requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+
+constexpr bool test() { return true; }
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.input/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.input/size.pass.cpp
new file mode 100644
index 0000000000000..30598b59031b7
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as.input/size.pass.cpp
@@ -0,0 +1,111 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <ranges>
+
+// template<input_range V>
+// requires view<V>
+// class as_input_view : public view_interface<as_input_view<V>>
+
+// constexpr auto size() requires sized_range<V>;
+// constexpr auto size() const requires sized_range<const V>;
+
+#include <array>
+#include <cassert>
+#include <ranges>
+#include <utility>
+
+#include "test_iterators.h"
+
+template <class T>
+concept HasSize = requires(T t) { t.size(); };
+
+constexpr bool test() {
+ {
+ struct SubtractableIteratorsView : std::ranges::view_base {
+ forward_iterator<int*> begin();
+ sized_sentinel<forward_iterator<int*>> end();
+ };
+
+ using ToInputViewT = std::ranges::as_input_view<SubtractableIteratorsView>;
+
+ static_assert(std::ranges::sized_range<ToInputViewT&>);
+ static_assert(!std::ranges::range<const ToInputViewT&>); // no begin()/end()
+
+ static_assert(HasSize<ToInputViewT&>);
+ static_assert(HasSize<ToInputViewT&&>);
+ static_assert(!HasSize<const ToInputViewT&>);
+ static_assert(!HasSize<const ToInputViewT&&>);
+ }
+ {
+ struct NonSizedView : std::ranges::view_base {
+ bidirectional_iterator<int*> begin();
+ bidirectional_iterator<int*> end();
+ };
+
+ using ToInputViewT = std::ranges::as_input_view<NonSizedView>;
+
+ static_assert(!HasSize<ToInputViewT&>);
+ static_assert(!HasSize<ToInputViewT&&>);
+ static_assert(!HasSize<const ToInputViewT&>);
+ static_assert(!HasSize<const ToInputViewT&&>);
+ }
+ {
+ struct SizedView : std::ranges::view_base {
+ bidirectional_iterator<int*> begin();
+ bidirectional_iterator<int*> end();
+
+ int size() const;
+ };
+
+ using ToInputViewT = std::ranges::as_input_view<SizedView>;
+
+ static_assert(std::ranges::sized_range<ToInputViewT&>);
+ static_assert(!std::ranges::range<const ToInputViewT&>); // no begin()/end()
+
+ static_assert(HasSize<ToInputViewT&>);
+ static_assert(HasSize<ToInputViewT&&>);
+ static_assert(!HasSize<const ToInputViewT&>); // not a view, therefore no size()
+ static_assert(!HasSize<const ToInputViewT&&>);
+ }
+ {
+ // Test an empty view.
+ int arr[] = {94};
+ auto view = std::ranges::as_input_view(std::ranges::subrange(arr, arr));
+
+ assert(view.size() == 0);
+ assert(std::as_const(view).size() == 0);
+ }
+ {
+ // Test a non-empty view.
+ int arr[] = {94};
+ auto view = std::ranges::as_input_view(std::ranges::subrange(arr, arr + 1));
+
+ assert(view.size() == 1);
+ assert(std::as_const(view).size() == 1);
+ }
+ {
+ // Test a non-view.
+ std::array<int, 2> arr = {94, 82};
+ auto view = std::ranges::as_input_view(std::move(arr));
+
+ assert(view.size() == 2);
+ assert(std::as_const(view).size() == 2);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 09126c14478bc..f68292925e3ce 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1113,6 +1113,11 @@ def add_version_header(tc):
"headers": ["ranges"],
"unimplemented": True,
},
+ {
+ "name": "__cpp_lib_ranges_as_input",
+ "values": {"c++26": 202502},
+ "headers": ["ranges"],
+ },
{
"name": "__cpp_lib_ranges_as_rvalue",
"values": {"c++23": 202207},
More information about the libcxx-commits
mailing list