[libcxx-commits] [libcxx] WIP [libc++][ranges] `std::views::as_input` (PR #151449)

Hristo Hristov via libcxx-commits libcxx-commits at lists.llvm.org
Thu Apr 23 00:49:28 PDT 2026


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

>From df36ef9d4a751467e465fb302dfcf49e586b713d 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://eel.is/c++draft/range.as.input
---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +
 libcxx/docs/ReleaseNotes/23.rst               |   2 +
 libcxx/docs/Status/Cxx2cPapers.csv            |   4 +-
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__ranges/as_input_view.h       | 226 ++++++++++++++
 libcxx/include/module.modulemap.in            |   3 +
 libcxx/include/ranges                         |  15 +
 libcxx/include/version                        |   2 +
 libcxx/modules/std/ranges.inc                 |  10 +
 .../range.to_input/nodiscard.verify.cpp       |  85 ++++++
 .../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              |  87 ++++++
 .../range.as_input/begin.pass.cpp             |  27 ++
 .../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    |  39 +++
 .../range.as_input/end.pass.cpp               |  26 ++
 .../range.as_input/general.pass.cpp           |  69 +++++
 .../range.as_input/iterator/base.pass.cpp     |  34 +++
 .../iterator/ctor.converting.pass.cpp         |  34 +++
 .../iterator/ctor.default.pass.cpp            |  80 +++++
 .../iterator/ctor.move.pass.cpp               |  34 +++
 .../iterator/iter_move.pass.cpp               |  34 +++
 .../iterator/iter_swap.pass.cpp               |  35 +++
 .../range.as_input/iterator/op.deref.pass.cpp |  33 +++
 .../range.as_input/iterator/op.eq.pass.cpp    |  33 +++
 .../iterator/op.increment.pass.cpp            |  34 +++
 .../range.as_input/iterator/op.minus.pass.cpp |  36 +++
 .../range.as_input/size.pass.cpp              | 111 +++++++
 .../generate_feature_test_macro_components.py |   5 +
 34 files changed, 1573 insertions(+), 3 deletions(-)
 create mode 100644 libcxx/include/__ranges/as_input_view.h
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.to_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 5f7d90e8eca5e..7c49f91e00dec 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -506,6 +506,8 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ranges_indices``                               ``202506L``
     ---------------------------------------------------------- -----------------
+    ``__cpp_lib_ranges_as_input``                              ``202502L``
+    ---------------------------------------------------------- -----------------
     ``__cpp_lib_ratio``                                        ``202306L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_rcu``                                          *unimplemented*
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 574deec92d4fb..c4c33ede93e8f 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -45,6 +45,8 @@ Implemented Papers
 - P2781R9: ``std::constant_wrapper`` (`Github <https://llvm.org/PR148179>`__)
 - P3978R3: ``constant_wrapper`` should unwrap on call and subscript (`Github <https://llvm.org/PR189605>`__)
 - P2164R9: ``views::enumerate`` (`Github <https://llvm.org/PR105251>`__)
+- P3137R3: ``views::as_input`` (`Github <https://llvm.org/PR127873>`__)
+- P3828R1: Rename the ``as_input`` view to ``as_input`` (`Github <https://llvm.org/PR189602>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index 6b706ab036e01..3847f1bd574c1 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.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|","22","`#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)","","","`#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|","23","`#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)","","","`#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 95d822ee8c0b3..ff18121a913c2 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -755,6 +755,7 @@ set(files
   __ranges/take_view.h
   __ranges/take_while_view.h
   __ranges/to.h
+  __ranges/as_input_view.h
   __ranges/transform_view.h
   __ranges/view_interface.h
   __ranges/views.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..66e1301272b00
--- /dev/null
+++ b/libcxx/include/__ranges/as_input_view.h
@@ -0,0 +1,226 @@
+// -*- 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_TO_INPUT_VIEW_H
+#define _LIBCPP___RANGES_TO_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.to.input.view]
+
+template <input_range _View>
+  requires view<_View>
+class as_input_view : public view_interface<as_input_view<_View>> {
+  _View __base_ = _View(); // exposition only
+
+  // [range.to.input.iterator], class template as_input_view::iterator
+  template <bool _Const>
+  class iterator; // exposition only
+
+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>; // exposition only
+
+  iterator_t<_Base> __current_ = iterator_t<_Base>(); // exposition only
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit iterator(iterator_t<_Base> __current)
+      : __current_(std::move(__current)) {} // exposition only
+
+  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_TO_INPUT_VIEW_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 234c230ee38d0..84291278bcd83 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1961,6 +1961,9 @@ module std [system] {
       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 782b4a77c0367..0845f3cfd76a3 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -407,6 +407,17 @@ namespace std::ranges {
   class chunk_by_view;                                                      // C++23
 
   namespace views { inline constexpr unspecified chunk_by = 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 {
@@ -502,6 +513,10 @@ namespace std {
 #    include <__ranges/zip_view.h>
 #  endif
 
+#  if _LIBCPP_STD_VER >= 26
+#    include <__ranges/as_input_view.h>
+#  endif
+
 #  include <version>
 
 // standard-mandated includes
diff --git a/libcxx/include/version b/libcxx/include/version
index b462fba39cd19..bbe68eca527c6 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -219,6 +219,7 @@ __cpp_lib_ranges_repeat                                 202207L <ranges>
 __cpp_lib_ranges_slide                                  202202L <ranges>
 __cpp_lib_ranges_starts_ends_with                       202106L <algorithm>
 __cpp_lib_ranges_to_container                           202202L <ranges>
+__cpp_lib_ranges_as_input                               202502L <ranges>
 __cpp_lib_ranges_zip                                    202110L <ranges> <tuple> <utility>
 __cpp_lib_ratio                                         202306L <ratio>
 __cpp_lib_raw_memory_algorithms                         201606L <memory>
@@ -613,6 +614,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 // # define __cpp_lib_philox_engine                        202406L
 // # define __cpp_lib_ranges_concat                        202403L
 # define __cpp_lib_ranges_indices                       202506L
+# define __cpp_lib_ranges_as_input                      202502L
 # define __cpp_lib_ratio                                202306L
 // # define __cpp_lib_rcu                                  202306L
 # define __cpp_lib_reference_wrapper                    202403L
diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc
index 97513bc686394..a870a2b1d7eee 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -363,6 +363,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.to_input/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.to_input/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..4235b1e782d15
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.to_input/nodiscard.verify.cpp
@@ -0,0 +1,85 @@
+//===----------------------------------------------------------------------===//
+//
+// 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> range;
+
+  // [range.to.input.view]
+
+  auto v = std::views::as_input(range);
+
+  // 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();
+
+  { // [range.to.input.iterator]
+
+    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 = v.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);
+  }
+
+  // [range.to.input.overview]
+
+  {
+    struct NonCommonRange {
+      auto begin() { return cpp17_input_iterator<int*>{nullptr}; };
+      auto end() { return sentinel_wrapper<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>);
+
+    // 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(range);
+  }
+}
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 7d763f877b321..fd6559959a5cf 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
@@ -72,6 +72,10 @@
 #    error "__cpp_lib_ranges_to_container 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_zip
 #    error "__cpp_lib_ranges_zip should not be defined before c++23"
 #  endif
@@ -130,6 +134,10 @@
 #    error "__cpp_lib_ranges_to_container 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_zip
 #    error "__cpp_lib_ranges_zip should not be defined before c++23"
 #  endif
@@ -188,6 +196,10 @@
 #    error "__cpp_lib_ranges_to_container 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_zip
 #    error "__cpp_lib_ranges_zip should not be defined before c++23"
 #  endif
@@ -249,6 +261,10 @@
 #    error "__cpp_lib_ranges_to_container 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_zip
 #    error "__cpp_lib_ranges_zip should not be defined before c++23"
 #  endif
@@ -355,6 +371,10 @@
 #    error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
 #  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_zip
 #    error "__cpp_lib_ranges_zip should be defined in c++23"
 #  endif
@@ -485,6 +505,13 @@
 #    error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
 #  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_zip
 #    error "__cpp_lib_ranges_zip 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 93a156bd9a6be..52cf0e2254841 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
@@ -708,6 +708,10 @@
 #    error "__cpp_lib_ranges_to_container 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_zip
 #    error "__cpp_lib_ranges_zip should not be defined before c++23"
 #  endif
@@ -1672,6 +1676,10 @@
 #    error "__cpp_lib_ranges_to_container 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_zip
 #    error "__cpp_lib_ranges_zip should not be defined before c++23"
 #  endif
@@ -2801,6 +2809,10 @@
 #    error "__cpp_lib_ranges_to_container 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_zip
 #    error "__cpp_lib_ranges_zip should not be defined before c++23"
 #  endif
@@ -4197,6 +4209,10 @@
 #    error "__cpp_lib_ranges_to_container 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_zip
 #    error "__cpp_lib_ranges_zip should not be defined before c++23"
 #  endif
@@ -5815,6 +5831,10 @@
 #    error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
 #  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_zip
 #    error "__cpp_lib_ranges_zip should be defined in c++23"
 #  endif
@@ -7754,6 +7774,13 @@
 #    error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
 #  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_zip
 #    error "__cpp_lib_ranges_zip 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 9443ca6c90a30..a0904b1b76ac9 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
@@ -138,5 +138,5 @@ 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::concat, a, a));
-// static_assert(test(std::views::to_input, a));
+static_assert(test(std::views::as_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..78a2a29236970
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as_input/base.pass.cpp
@@ -0,0 +1,87 @@
+//===----------------------------------------------------------------------===//
+//
+// 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..71c04adac99e5
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as_input/begin.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>;
+
+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..c4a1a8c57c499
--- /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>>>);
\ No newline at end of file
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..79e0479e5f07c
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as_input/enable_borrowed_range.compile.pass.cpp
@@ -0,0 +1,39 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+#include <tuple>
+
+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..87a20dd5f994e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as_input/end.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 auto end() requires (!simple-view<V>);
+//    constexpr auto end() const requires range<const V>;
+
+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..8d06cef9cdd53
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as_input/general.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
+//
+//===----------------------------------------------------------------------===//
+
+// 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 <cassert>
+#include <ranges>
+#include <string>
+#include <vector>
+#include <algorithm>
+
+template <class R, class I>
+constexpr bool isEqual(R& r, I i) {
+  for (auto e : r)
+    if (e != *i++)
+      return false;
+
+  return true;
+}
+
+constexpr bool test() {
+  std::vector<std::string> vec{"Hello", ",", " ", "World", "!"};
+  std::string expectedStr = "Hello, World!";
+
+  {
+    auto view = vec | std::views::join;
+    assert(isEqual(view, expectedStr.begin()));
+  }
+  { // Test as_input_view with a vector of strings.
+    auto view = vec | std::views::as_input | std::views::join;
+    assert(isEqual(view, expectedStr.begin()));
+  }
+
+  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..9cb24c8e8f50c
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as_input/iterator/base.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
+
+//     constexpr iterator_t<Base> base() &&;
+//     constexpr const iterator_t<Base>& base() const & noexcept;
+
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+
+#include "test_iterators.h"
+
+constexpr bool test() { return true; }
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
\ No newline at end of file
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..231d57334cde1
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as_input/iterator/ctor.converting.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
+
+//    constexpr iterator(iterator<!Const> i)
+//      requires Const && convertible_to<iterator_t<V>, iterator_t<Base>>;
+
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+
+#include "test_iterators.h"
+
+constexpr bool test() { return true; }
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
\ No newline at end of file
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..61541e23cc21d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as_input/iterator/ctor.default.pass.cpp
@@ -0,0 +1,80 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <ranges>
+#include <type_traits>
+
+#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 == 0); // DefaultInitializableIterator has to be initialized to have value 0.
+  }
+  {
+    ToInputViewIteratorT<DefaultInitializableIteratorView> it = {};
+    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..fea70f06c7488
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as_input/iterator/ctor.move.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
+
+//    iterator(iterator&&) = default;
+//    iterator& operator=(iterator&&) = default;
+
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+
+#include "test_iterators.h"
+
+constexpr bool test() { return true; }
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
\ No newline at end of file
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..9198c55543f1c
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as_input/iterator/iter_move.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 range_rvalue_reference_t<Base> iter_move(const iterator& i)
+//      noexcept(noexcept(ranges::iter_move(i.current_)));
+
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+
+#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..c7b0537437ffc
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as_input/iterator/iter_swap.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 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 <type_traits>
+
+#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..8c73d1133ca52
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as_input/iterator/op.deref.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 decltype(auto) operator*() const
+
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+
+#include "test_iterators.h"
+
+constexpr bool test() { return true; }
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
\ No newline at end of file
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..68eb2cf9899ab
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as_input/iterator/op.eq.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 bool operator==(const iterator& x, const sentinel_t<Base>& y);
+
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+
+#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..9df3a817d144a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as_input/iterator/op.increment.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
+
+//    constexpr iterator& operator++();
+//    constexpr void operator++(int);
+
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+
+#include "test_iterators.h"
+
+constexpr bool test() { return true; }
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
\ No newline at end of file
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..5fb4c20d1c504
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.as_input/iterator/op.minus.pass.cpp
@@ -0,0 +1,36 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <type_traits>
+
+#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 8f421e8cbd0f0..c277d495f2633 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1105,6 +1105,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