diff --git a/libcxx/include/iterator b/libcxx/include/iterator
--- a/libcxx/include/iterator
+++ b/libcxx/include/iterator
@@ -59,6 +59,11 @@
 using iter_rvalue_reference_t
   = decltype(ranges::iter_move(declval<T&>()));
 
+// [iterator.concepts], iterator concepts
+// [iterator.concept.readable], concept indirectly_­readable
+template<class In>
+  concept indirectly_readable = see below;                // since C++20
+
 template<class Category, class T, class Distance = ptrdiff_t,
          class Pointer = T*, class Reference = T&>
 struct iterator
@@ -2508,6 +2513,23 @@
 requires requires(_Tp& __t) { { ranges::iter_move(__t) } -> __can_reference; }
 using iter_rvalue_reference_t = decltype(ranges::iter_move(declval<_Tp&>()));
 
+// [iterator.concept.readable]
+template<class _In>
+concept __indirectly_readable_impl =
+  requires(const _In __in) {
+    typename iter_value_t<_In>;
+    typename iter_reference_t<_In>;
+    typename iter_rvalue_reference_t<_In>;
+    { *__in } -> same_as<iter_reference_t<_In>>;
+    { ranges::iter_move(__in) } -> same_as<iter_rvalue_reference_t<_In>>;
+  } &&
+  common_reference_with<iter_reference_t<_In>&&, iter_value_t<_In>&> &&
+  common_reference_with<iter_reference_t<_In>&&, iter_rvalue_reference_t<_In>&&> &&
+  common_reference_with<iter_rvalue_reference_t<_In>&&, const iter_value_t<_In>&>;
+
+template<class _In>
+concept indirectly_readable = __indirectly_readable_impl<remove_cvref_t<_In>>;
+
 #undef _LIBCPP_NOEXCEPT_RETURN
 #endif // !defined(_LIBCPP_HAS_NO_RANGES)
 
diff --git a/libcxx/test/std/iterators/iterator.requirements/iterator.concepts/iterator.concept.readable/indirectly_readable.compile.pass.cpp b/libcxx/test/std/iterators/iterator.requirements/iterator.concepts/iterator.concept.readable/indirectly_readable.compile.pass.cpp
new file mode 100644
--- /dev/null
+++ b/libcxx/test/std/iterators/iterator.requirements/iterator.concepts/iterator.concept.readable/indirectly_readable.compile.pass.cpp
@@ -0,0 +1,319 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-no-concepts
+// UNSUPPORTED: gcc-10
+
+// template<class In>
+// concept indirectly_readable;
+
+#include <iterator>
+
+#include <array>
+#include <concepts>
+#include <deque>
+#ifndef _LIBCPP_HAS_NO_FILESYSTEM_LIBRARY
+#include <filesystem>
+#endif
+#include <forward_list>
+#include <istream>
+#include <list>
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <string_view>
+#include <optional>
+#include <ostream>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include "../read_write.h"
+
+template <class In>
+[[nodiscard]] constexpr bool check_indirectly_readable() {
+  constexpr bool result = std::indirectly_readable<In>;
+  static_assert(std::indirectly_readable<In const> == result);
+  static_assert(std::indirectly_readable<In volatile> == result);
+  static_assert(std::indirectly_readable<In const volatile> == result);
+  static_assert(std::indirectly_readable<In const&> == result);
+  static_assert(std::indirectly_readable<In volatile&> == result);
+  static_assert(std::indirectly_readable<In const volatile&> == result);
+  static_assert(std::indirectly_readable<In const&&> == result);
+  static_assert(std::indirectly_readable<In volatile&&> == result);
+  static_assert(std::indirectly_readable<In const volatile&&> == result);
+  return result;
+}
+
+static_assert(check_indirectly_readable<traditional_indirection>());
+
+struct alternative_indirection {
+  using element_type = long;
+  element_type& operator*() const;
+};
+static_assert(check_indirectly_readable<alternative_indirection>());
+
+static_assert(check_indirectly_readable<proxy_indirection>());
+static_assert(check_indirectly_readable<read_only_indirection>());
+
+struct indirection_mismatch {
+  using value_type = int;
+  float& operator*() const;
+};
+static_assert(!std::same_as<std::iter_value_t<indirection_mismatch>,
+                            std::iter_reference_t<indirection_mismatch> > &&
+              check_indirectly_readable<indirection_mismatch>());
+static_assert(!check_indirectly_readable<missing_dereference>());
+
+// `iter_rvalue_reference_t` can't be missing unless the dereference operator is also missing.
+
+struct iter_move_mismatch {
+  using value_type = int;
+  value_type& operator*() const;
+
+  friend float& iter_move(iter_move_mismatch&);
+};
+static_assert(!check_indirectly_readable<iter_move_mismatch>());
+
+struct indirection_and_iter_move_mismatch {
+  using value_type = int;
+  float& operator*() const;
+
+  friend unsigned long long& iter_move(indirection_and_iter_move_mismatch&);
+};
+static_assert(!check_indirectly_readable<indirection_and_iter_move_mismatch>());
+
+struct missing_iter_value_t {
+  int operator*() const;
+};
+static_assert(!check_indirectly_readable<missing_iter_value_t>());
+
+struct lvalue_ref_has_no_common_type_with_rvalue_ref {};
+
+struct iter_ref1 {};
+namespace std {
+template <>
+struct common_reference<iter_ref1&, iter_ref1&&> {};
+
+template <>
+struct common_reference<iter_ref1&&, iter_ref1&> {};
+} // namespace std
+static_assert(!std::common_reference_with<iter_ref1&, iter_ref1&&>);
+
+struct bad_iter_reference_t {
+  using value_type = int;
+  iter_ref1& operator*() const;
+};
+static_assert(!check_indirectly_readable<bad_iter_reference_t>());
+
+struct iter_ref2 {};
+struct iter_rvalue_ref {};
+
+struct
+    no_common_reference_between_rvalue_ref_to_iter_ref_and_rvalue_ref_to_iter_rvalue_ref {
+  using value_type = iter_ref2;
+  iter_ref2& operator*() const;
+  friend iter_rvalue_ref&& iter_move(
+      no_common_reference_between_rvalue_ref_to_iter_ref_and_rvalue_ref_to_iter_rvalue_ref);
+};
+static_assert(
+    !check_indirectly_readable<
+        no_common_reference_between_rvalue_ref_to_iter_ref_and_rvalue_ref_to_iter_rvalue_ref>());
+
+struct iter_ref3 {
+  operator iter_rvalue_ref() const;
+};
+namespace std {
+template <template <class> class XQual, template <class> class YQual>
+struct basic_common_reference<iter_ref3, iter_rvalue_ref, XQual, YQual> {
+  using type = iter_rvalue_ref;
+};
+template <template <class> class XQual, template <class> class YQual>
+struct basic_common_reference<iter_rvalue_ref, iter_ref3, XQual, YQual> {
+  using type = iter_rvalue_ref;
+};
+} // namespace std
+static_assert(std::common_reference_with<iter_ref3&&, iter_rvalue_ref&&>);
+
+struct different_reference_types_but_share_common_reference {
+  using value_type = iter_ref3;
+  iter_ref3& operator*() const;
+  friend iter_rvalue_ref&&
+      iter_move(different_reference_types_but_share_common_reference);
+};
+static_assert(check_indirectly_readable<
+              different_reference_types_but_share_common_reference>());
+
+struct iter_ref4 {
+  operator iter_rvalue_ref() const;
+};
+namespace std {
+template <template <class> class XQual, template <class> class YQual>
+struct basic_common_reference<iter_ref4, iter_rvalue_ref, XQual, YQual> {
+  using type = iter_rvalue_ref;
+};
+template <template <class> class XQual, template <class> class YQual>
+struct basic_common_reference<iter_rvalue_ref, iter_ref4, XQual, YQual> {
+  using type = iter_rvalue_ref;
+};
+
+template <>
+struct common_reference<iter_ref4 const&, iter_rvalue_ref&&> {};
+template <>
+struct common_reference<iter_rvalue_ref&&, iter_ref4 const&> {};
+} // namespace std
+static_assert(std::common_reference_with<iter_ref4&&, iter_rvalue_ref&&>);
+static_assert(!std::common_reference_with<iter_ref4 const&, iter_rvalue_ref&&>);
+
+struct different_reference_types_without_common_reference_to_const {
+  using value_type = iter_ref4;
+  iter_ref4& operator*() const;
+  friend iter_rvalue_ref&&
+      iter_move(different_reference_types_without_common_reference_to_const);
+};
+static_assert(!check_indirectly_readable<
+              different_reference_types_without_common_reference_to_const>());
+
+namespace standard_types {
+static_assert(check_indirectly_readable<int*>());
+static_assert(check_indirectly_readable<int const*>());
+static_assert(check_indirectly_readable<int volatile*>());
+static_assert(check_indirectly_readable<int const volatile*>());
+
+// <array>
+static_assert(check_indirectly_readable<std::array<int, 10>::iterator>());
+static_assert(check_indirectly_readable<std::array<int, 10>::const_iterator>());
+static_assert(
+    check_indirectly_readable<std::array<int, 10>::reverse_iterator>());
+static_assert(
+    check_indirectly_readable<std::array<int, 10>::const_reverse_iterator>());
+
+// <deque>
+static_assert(check_indirectly_readable<std::deque<int>::iterator>());
+static_assert(check_indirectly_readable<std::deque<int>::const_iterator>());
+static_assert(check_indirectly_readable<std::deque<int>::reverse_iterator>());
+static_assert(
+    check_indirectly_readable<std::deque<int>::const_reverse_iterator>());
+
+// <filesystem>
+#ifndef _LIBCPP_HAS_NO_FILESYSTEM_LIBRARY
+static_assert(check_indirectly_readable<std::filesystem::directory_iterator>());
+static_assert(
+    check_indirectly_readable<std::filesystem::recursive_directory_iterator>());
+#endif
+
+// <forward_list>
+static_assert(check_indirectly_readable<std::forward_list<int>::iterator>());
+static_assert(
+    check_indirectly_readable<std::forward_list<int>::const_iterator>());
+
+// <iterator>
+static_assert(!check_indirectly_readable<
+              std::back_insert_iterator<std::vector<int> > >());
+static_assert(!check_indirectly_readable<
+              std::front_insert_iterator<std::deque<int> > >());
+static_assert(
+    !check_indirectly_readable<std::insert_iterator<std::deque<int> > >());
+static_assert(check_indirectly_readable<std::istream_iterator<int, char> >());
+static_assert(check_indirectly_readable<std::istreambuf_iterator<char> >());
+static_assert(check_indirectly_readable<
+              std::move_iterator<std::vector<int>::iterator> >());
+static_assert(!check_indirectly_readable<std::ostream_iterator<int, char> >());
+static_assert(
+    !check_indirectly_readable<std::ostreambuf_iterator<int, char> >());
+
+// <list>
+static_assert(check_indirectly_readable<std::list<int>::iterator>());
+static_assert(check_indirectly_readable<std::list<int>::const_iterator>());
+static_assert(check_indirectly_readable<std::list<int>::reverse_iterator>());
+static_assert(
+    check_indirectly_readable<std::list<int>::const_reverse_iterator>());
+
+// <map>
+static_assert(check_indirectly_readable<std::map<int, int>::iterator>());
+static_assert(check_indirectly_readable<std::map<int, int>::const_iterator>());
+static_assert(
+    check_indirectly_readable<std::map<int, int>::reverse_iterator>());
+static_assert(
+    check_indirectly_readable<std::map<int, int>::const_reverse_iterator>());
+
+static_assert(check_indirectly_readable<std::multimap<int, int>::iterator>());
+static_assert(
+    check_indirectly_readable<std::multimap<int, int>::const_iterator>());
+static_assert(
+    check_indirectly_readable<std::multimap<int, int>::reverse_iterator>());
+static_assert(check_indirectly_readable<
+              std::multimap<int, int>::const_reverse_iterator>());
+
+// <memory>
+static_assert(check_indirectly_readable<std::shared_ptr<int> >());
+static_assert(!check_indirectly_readable<std::shared_ptr<void> >());
+static_assert(check_indirectly_readable<std::unique_ptr<int> >());
+static_assert(!check_indirectly_readable<std::unique_ptr<void> >());
+
+// <optional>
+static_assert(!check_indirectly_readable<std::optional<int> >());
+
+// <set>
+static_assert(check_indirectly_readable<std::set<int>::iterator>());
+static_assert(check_indirectly_readable<std::set<int>::const_iterator>());
+static_assert(check_indirectly_readable<std::set<int>::reverse_iterator>());
+static_assert(
+    check_indirectly_readable<std::set<int>::const_reverse_iterator>());
+
+static_assert(check_indirectly_readable<std::multiset<int>::iterator>());
+static_assert(check_indirectly_readable<std::multiset<int>::const_iterator>());
+static_assert(
+    check_indirectly_readable<std::multiset<int>::reverse_iterator>());
+static_assert(
+    check_indirectly_readable<std::multiset<int>::const_reverse_iterator>());
+
+// <string>
+static_assert(check_indirectly_readable<std::string::iterator>());
+static_assert(check_indirectly_readable<std::string::const_iterator>());
+static_assert(check_indirectly_readable<std::string::reverse_iterator>());
+static_assert(check_indirectly_readable<std::string::const_reverse_iterator>());
+
+// <string_view>
+static_assert(check_indirectly_readable<std::string_view::iterator>());
+static_assert(check_indirectly_readable<std::string_view::const_iterator>());
+static_assert(check_indirectly_readable<std::string_view::reverse_iterator>());
+static_assert(
+    check_indirectly_readable<std::string_view::const_reverse_iterator>());
+
+// <unordered_map>
+static_assert(
+    check_indirectly_readable<std::unordered_map<int, int>::iterator>());
+static_assert(
+    check_indirectly_readable<std::unordered_map<int, int>::const_iterator>());
+
+static_assert(
+    check_indirectly_readable<std::unordered_multimap<int, int>::iterator>());
+static_assert(check_indirectly_readable<
+              std::unordered_multimap<int, int>::const_iterator>());
+
+// <unordered_set>
+static_assert(check_indirectly_readable<std::unordered_set<int>::iterator>());
+static_assert(
+    check_indirectly_readable<std::unordered_set<int>::const_iterator>());
+
+static_assert(
+    check_indirectly_readable<std::unordered_multiset<int, int>::iterator>());
+static_assert(check_indirectly_readable<
+              std::unordered_multiset<int, int>::const_iterator>());
+
+// <vector>
+static_assert(check_indirectly_readable<std::vector<int>::iterator>());
+static_assert(check_indirectly_readable<std::vector<int>::const_iterator>());
+static_assert(check_indirectly_readable<std::vector<int>::reverse_iterator>());
+static_assert(
+    check_indirectly_readable<std::vector<int>::const_reverse_iterator>());
+static_assert(!check_indirectly_readable<std::vector<int> >());
+} // namespace standard_types
diff --git a/libcxx/test/std/iterators/iterator.requirements/iterator.concepts/read_write.h b/libcxx/test/std/iterators/iterator.requirements/iterator.concepts/read_write.h
new file mode 100644
--- /dev/null
+++ b/libcxx/test/std/iterators/iterator.requirements/iterator.concepts/read_write.h
@@ -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
+//
+//===----------------------------------------------------------------------===//
+#ifndef LIBCPP_TEST_STD_ITERATORS_ITERATOR_REQUIREMENTS_ITERATOR_CONCEPTS_READ_WRITE_H
+#define LIBCPP_TEST_STD_ITERATORS_ITERATOR_REQUIREMENTS_ITERATOR_CONCEPTS_READ_WRITE_H
+
+struct traditional_indirection {
+  using value_type = int;
+  value_type& operator*() const;
+};
+
+struct proxy_indirection {
+  using value_type = int;
+  value_type operator*() const;
+};
+
+struct read_only_indirection {
+  using value_type = int const;
+  value_type& operator*() const;
+};
+
+// doubles as missing_iter_reference_t
+struct missing_dereference {
+  using value_type = int;
+};
+
+#endif // LIBCPP_TEST_STD_ITERATORS_ITERATOR_REQUIREMENTS_ITERATOR_CONCEPTS_READ_WRITE_H