[libcxx-commits] [libcxx] [libc++] Implement `views::join_with` (PR #65536)

Jakub Mazurkiewicz via libcxx-commits libcxx-commits at lists.llvm.org
Mon Dec 16 15:59:43 PST 2024


https://github.com/JMazurkiewicz updated https://github.com/llvm/llvm-project/pull/65536

>From 698bdd62d66ca4c0e690d08ebc9ad70f5384a1bc Mon Sep 17 00:00:00 2001
From: Jakub Mazurkiewicz <mazkuba3 at gmail.com>
Date: Sat, 3 Feb 2024 00:42:52 +0100
Subject: [PATCH 01/15] [libc++] Implement `views::join_with`

* Implement "P2441R2 `views::join_with`" (https://wg21.link/P2441R2)
* Complete implementation of "P2711R1 Making multi-param constructors of views explicit" (https://wg21.link/P2711R1)
* Complete implementation of "P2770R0 Stashing stashing iterators for proper flattening" (https://wg21.link/P2770R0)
---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +-
 libcxx/docs/ReleaseNotes/19.rst               |   3 +
 libcxx/docs/Status/Cxx23.rst                  |   4 +-
 libcxx/docs/Status/Cxx23Papers.csv            |   6 +-
 libcxx/docs/Status/RangesMajorFeatures.csv    |   2 +-
 libcxx/docs/Status/RangesViews.csv            |   2 +-
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__ranges/join_with_view.h      | 456 ++++++++++++++++++
 libcxx/include/module.modulemap               |   4 +
 libcxx/include/ranges                         |  13 +
 libcxx/include/version                        |   2 +-
 libcxx/modules/std/ranges.inc                 |   7 +-
 .../deref.nodiscard.verify.cpp                |  28 ++
 .../eq.nodiscard.verify.cpp                   |  30 ++
 .../iter_move.nodiscard.verify.cpp            |  28 ++
 .../no_unique_address.compile.pass.cpp        |  38 ++
 .../adaptor.nodiscard.verify.cpp              |  33 ++
 .../eq.nodiscard.verify.cpp                   |  35 ++
 .../base.nodiscard.verify.cpp                 |  30 ++
 .../begin.nodiscard.verify.cpp                |  28 ++
 .../end.nodiscard.verify.cpp                  |  28 ++
 .../no_unique_address.compile.pass.cpp        |  40 ++
 .../test/libcxx/transitive_includes/cxx03.csv |   1 +
 .../test/libcxx/transitive_includes/cxx11.csv |   1 +
 .../test/libcxx/transitive_includes/cxx14.csv |   1 +
 .../test/libcxx/transitive_includes/cxx17.csv |   1 +
 .../test/libcxx/transitive_includes/cxx20.csv |   1 +
 .../ranges.version.compile.pass.cpp           |  32 +-
 .../version.version.compile.pass.cpp          |  32 +-
 .../ctor.default.pass.cpp                     |  77 +++
 .../ctor.not_const.pass.cpp                   | 104 ++++
 .../decrement.pass.cpp                        | 283 +++++++++++
 .../range.join.with.iterator/deref.pass.cpp   | 144 ++++++
 .../range.join.with.iterator/eq.pass.cpp      | 256 ++++++++++
 .../increment.pass.cpp                        | 320 ++++++++++++
 .../iter_move.pass.cpp                        | 340 +++++++++++++
 .../iter_swap.pass.cpp                        |  81 ++++
 .../types.compile.pass.cpp                    | 456 ++++++++++++++++++
 .../range.join.with.overview/adaptor.pass.cpp | 359 ++++++++++++++
 .../range.join.with.overview/example.pass.cpp |  45 ++
 .../ctor.default.pass.cpp                     |  37 ++
 .../ctor.non_const.pass.cpp                   |  71 +++
 .../range.join.with.sentinel/eq.pass.cpp      | 101 ++++
 .../range.join.with.view/base.pass.cpp        | 148 ++++++
 .../range.join.with.view/begin.pass.cpp       | 257 ++++++++++
 .../constraints.compile.pass.cpp              | 266 ++++++++++
 .../ctad.compile.pass.cpp                     | 230 +++++++++
 .../ctor.default.pass.cpp                     |  73 +++
 .../ctor.range.element.pass.cpp               | 244 ++++++++++
 .../ctor.range.pattern.pass.cpp               | 111 +++++
 .../range.join.with.view/end.pass.cpp         | 244 ++++++++++
 .../inheritance.compile.pass.cpp              |  38 ++
 .../range.adaptors/range.join.with/types.h    | 267 ++++++++++
 .../generate_feature_test_macro_components.py |   1 -
 54 files changed, 5385 insertions(+), 57 deletions(-)
 create mode 100644 libcxx/include/__ranges/join_with_view.h
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/deref.nodiscard.verify.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/eq.nodiscard.verify.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_move.nodiscard.verify.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/no_unique_address.compile.pass.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.overview/adaptor.nodiscard.verify.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.sentinel/eq.nodiscard.verify.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/base.nodiscard.verify.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/begin.nodiscard.verify.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/end.nodiscard.verify.cpp
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/no_unique_address.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/ctor.default.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/ctor.not_const.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/decrement.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/deref.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/eq.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/increment.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_move.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_swap.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/types.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.overview/adaptor.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.overview/example.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.sentinel/ctor.default.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.sentinel/ctor.non_const.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.sentinel/eq.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/base.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/begin.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/constraints.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/ctad.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/ctor.default.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/ctor.range.element.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/ctor.range.pattern.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/end.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/inheritance.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/types.h

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 72f624fa746b41..2b382db2e5b4f3 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -362,7 +362,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ranges_iota``                                  *unimplemented*
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_ranges_join_with``                             *unimplemented*
+    ``__cpp_lib_ranges_join_with``                             ``202202L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ranges_repeat``                                ``202207L``
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/19.rst b/libcxx/docs/ReleaseNotes/19.rst
index 71de10abb6eaa8..81aaa54b5c2310 100644
--- a/libcxx/docs/ReleaseNotes/19.rst
+++ b/libcxx/docs/ReleaseNotes/19.rst
@@ -53,6 +53,9 @@ Implemented Papers
 - P2713R1 - Escaping improvements in ``std::format``
 - P2231R1 - Missing ``constexpr`` in ``std::optional`` and ``std::variant``
 - P0019R8 - ``std::atomic_ref``
+- P2441R2 - ``views::join_with``
+- P2711R1 - Making multi-param constructors of ``views`` ``explicit``
+- P2770R0 - Stashing stashing ``iterators`` for proper flattening
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx23.rst b/libcxx/docs/Status/Cxx23.rst
index 23d30c8128d71e..a10f4ef9bf0719 100644
--- a/libcxx/docs/Status/Cxx23.rst
+++ b/libcxx/docs/Status/Cxx23.rst
@@ -44,8 +44,6 @@ Paper Status
    .. [#note-P1413R3] P1413R3: ``std::aligned_storage_t`` and ``std::aligned_union_t`` are marked deprecated, but
       clang doesn't issue a diagnostic for deprecated using template declarations.
    .. [#note-P2520R0] P2520R0: Libc++ implemented this paper as a DR in C++20 as well.
-   .. [#note-P2711R1] P2711R1: ``join_with_view`` hasn't been done yet since this type isn't implemented yet.
-   .. [#note-P2770R0] P2770R0: ``join_with_view`` hasn't been done yet since this type isn't implemented yet.
    .. [#note-P2693R1] P2693R1: The formatter for ``std::thread::id`` is implemented.
       The formatter for ``stacktrace`` is not implemented, since ``stacktrace`` is
       not implemented yet.
@@ -63,5 +61,5 @@ Library Working Group Issues Status
 .. note::
 
    .. [#note-LWG3750] LWG3750 Only ``__cpp_lib_format_ranges`` is fully implemented.
-   .. [#note-LWG3798] LWG3798: ``join_with_view``, ``zip_transform_view``, and ``adjacent_transform_view`` haven't been done yet since these types aren't implemented yet.
+   .. [#note-LWG3798] LWG3798: ``zip_transform_view`` and ``adjacent_transform_view`` haven't been done yet since these types aren't implemented yet.
    .. [#note-LWG3036] LWG3036: This issue was reverted by P2875R4
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 36d7f78285aa9c..761cfd1313daac 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -47,7 +47,7 @@
 "`P2273R3 <https://wg21.link/P2273R3>`__","LWG","Making ``std::unique_ptr`` constexpr","February 2022","|Complete|","16.0"
 "`P2387R3 <https://wg21.link/P2387R3>`__","LWG","Pipe support for user-defined range adaptors","February 2022","|Complete|","19.0","|ranges|"
 "`P2440R1 <https://wg21.link/P2440R1>`__","LWG","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","February 2022","","","|ranges|"
-"`P2441R2 <https://wg21.link/P2441R2>`__","LWG","``views::join_with``","February 2022","|In Progress|","","|ranges|"
+"`P2441R2 <https://wg21.link/P2441R2>`__","LWG","``views::join_with``","February 2022","|Complete|","19.0","|ranges|"
 "`P2442R1 <https://wg21.link/P2442R1>`__","LWG","Windowing range adaptors: ``views::chunk`` and ``views::slide``","February 2022","","","|ranges|"
 "`P2443R1 <https://wg21.link/P2443R1>`__","LWG","``views::chunk_by``","February 2022","|Complete|","18.0","|ranges|"
 "","","","","","",""
@@ -104,9 +104,9 @@
 "`P2708R1 <https://wg21.link/P2708R1>`__","LWG", "No Further Fundamentals TSes", "November 2022","|Nothing to do|","",""
 "","","","","","",""
 "`P0290R4 <https://wg21.link/P0290R4>`__","LWG", "``apply()`` for ``synchronized_value<T>``","February 2023","","","|concurrency TS|"
-"`P2770R0 <https://wg21.link/P2770R0>`__","LWG", "Stashing stashing ``iterators`` for proper flattening","February 2023","|Partial| [#note-P2770R0]_","","|ranges|"
+"`P2770R0 <https://wg21.link/P2770R0>`__","LWG", "Stashing stashing ``iterators`` for proper flattening","February 2023","|Complete|","19.0","|ranges|"
 "`P2164R9 <https://wg21.link/P2164R9>`__","LWG", "``views::enumerate``","February 2023","","","|ranges|"
-"`P2711R1 <https://wg21.link/P2711R1>`__","LWG", "Making multi-param constructors of ``views`` ``explicit``","February 2023","|In Progress| [#note-P2711R1]_","","|ranges|"
+"`P2711R1 <https://wg21.link/P2711R1>`__","LWG", "Making multi-param constructors of ``views`` ``explicit``","February 2023","|Complete|","19.0","|ranges|"
 "`P2609R3 <https://wg21.link/P2609R3>`__","LWG", "Relaxing Ranges Just A Smidge","February 2023","","","|ranges|"
 "`P2713R1 <https://wg21.link/P2713R1>`__","LWG", "Escaping improvements in ``std::format``","February 2023","|Complete|","19.0","|format|"
 "`P2675R1 <https://wg21.link/P2675R1>`__","LWG", "``format``'s width estimation is too approximate and not forward compatible","February 2023","|Complete|","17.0","|format|"
diff --git a/libcxx/docs/Status/RangesMajorFeatures.csv b/libcxx/docs/Status/RangesMajorFeatures.csv
index d00fbce9edf489..be67d2d94bf97b 100644
--- a/libcxx/docs/Status/RangesMajorFeatures.csv
+++ b/libcxx/docs/Status/RangesMajorFeatures.csv
@@ -2,4 +2,4 @@ Standard,Name,Assignee,CL,Status
 C++23,`ranges::to <https://wg21.link/P1206R7>`_,Konstantin Varlamov,`D142335 <https://reviews.llvm.org/D142335>`_,Complete
 C++23,`Pipe support for user-defined range adaptors <https://wg21.link/P2387R3>`_,"Louis Dionne, Jakub Mazurkiewicz, and Xiaoyang Liu",Various,Complete
 C++23,`Formatting Ranges <https://wg21.link/P2286R8>`_,Mark de Wever,Various,Complete
-C++20,`Stashing stashing iterators for proper flattening <https://wg21.link/P2770R0>`_,Jakub Mazurkiewicz,Various,In progress
+C++20,`Stashing stashing iterators for proper flattening <https://wg21.link/P2770R0>`_,Jakub Mazurkiewicz,Various,Complete
diff --git a/libcxx/docs/Status/RangesViews.csv b/libcxx/docs/Status/RangesViews.csv
index f141656eb131a2..fac950134ee88f 100644
--- a/libcxx/docs/Status/RangesViews.csv
+++ b/libcxx/docs/Status/RangesViews.csv
@@ -28,7 +28,7 @@ C++23,`zip <https://wg21.link/P2321R2>`_,Hui Xie,`D122806 <https://llvm.org/D122
 C++23,`zip_transform <https://wg21.link/P2321R2>`_,Hui Xie,No patch yet,Not started
 C++23,`adjacent <https://wg21.link/P2321R2>`_,Hui Xie,No patch yet,Not started
 C++23,`adjacent_transform <https://wg21.link/P2321R2>`_,Hui Xie,No patch yet,Not started
-C++23,`join_with <https://wg21.link/P2441R2>`_,Jakub Mazurkiewicz,`65536 <https://github.com/llvm/llvm-project/pull/65536>`_,In progress
+C++23,`join_with <https://wg21.link/P2441R2>`_,Jakub Mazurkiewicz,`65536 <https://github.com/llvm/llvm-project/pull/65536>`_,✅
 C++23,`slide <https://wg21.link/P2442R1>`_,Will Hawkins,`67146 <https://github.com/llvm/llvm-project/pull/67146>`_,In Progress
 C++23,`chunk <https://wg21.link/P2442R1>`_,Unassigned,No patch yet,Not started
 C++23,`chunk_by <https://wg21.link/P2443R1>`_,Jakub Mazurkiewicz,`D144767 <https://llvm.org/D144767>`_,✅
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 8d0ffd6ed725bd..67a22550701f2a 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -651,6 +651,7 @@ set(files
   __ranges/iota_view.h
   __ranges/istream_view.h
   __ranges/join_view.h
+  __ranges/join_with_view.h
   __ranges/lazy_split_view.h
   __ranges/movable_box.h
   __ranges/non_propagating_cache.h
diff --git a/libcxx/include/__ranges/join_with_view.h b/libcxx/include/__ranges/join_with_view.h
new file mode 100644
index 00000000000000..3918b64ebaf1ad
--- /dev/null
+++ b/libcxx/include/__ranges/join_with_view.h
@@ -0,0 +1,456 @@
+// -*- 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_JOIN_WITH_VIEW_H
+#define _LIBCPP___RANGES_JOIN_WITH_VIEW_H
+
+#include <__concepts/common_reference_with.h>
+#include <__concepts/common_with.h>
+#include <__concepts/constructible.h>
+#include <__concepts/convertible_to.h>
+#include <__concepts/derived_from.h>
+#include <__config>
+#include <__functional/bind_back.h>
+#include <__iterator/concepts.h>
+#include <__iterator/incrementable_traits.h>
+#include <__iterator/iter_move.h>
+#include <__iterator/iter_swap.h>
+#include <__iterator/iterator_traits.h>
+#include <__memory/addressof.h>
+#include <__ranges/access.h>
+#include <__ranges/all.h>
+#include <__ranges/concepts.h>
+#include <__ranges/non_propagating_cache.h>
+#include <__ranges/range_adaptor.h>
+#include <__ranges/single_view.h>
+#include <__ranges/view_interface.h>
+#include <__type_traits/conditional.h>
+#include <__type_traits/decay.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/maybe_const.h>
+#include <__utility/as_const.h>
+#include <__utility/as_lvalue.h>
+#include <__utility/empty.h>
+#include <__utility/forward.h>
+#include <__utility/move.h>
+#include <variant>
+
+#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 >= 23
+
+namespace ranges {
+template <class _Range, class _Pattern>
+concept __compatible_joinable_ranges =
+    common_with<range_value_t<_Range>, range_value_t<_Pattern>> &&
+    common_reference_with<range_reference_t<_Range>, range_reference_t<_Pattern>> &&
+    common_reference_with<range_rvalue_reference_t<_Range>, range_rvalue_reference_t<_Pattern>>;
+
+template <class _Range>
+concept __bidirectional_common = bidirectional_range<_Range> && common_range<_Range>;
+
+template <input_range _View, forward_range _Pattern>
+  requires view<_View> && input_range<range_reference_t<_View>> && view<_Pattern> &&
+           __compatible_joinable_ranges<range_reference_t<_View>, _Pattern>
+class join_with_view : public view_interface<join_with_view<_View, _Pattern>> {
+  using _InnerRng = range_reference_t<_View>;
+
+  _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View();
+
+  static constexpr bool _UseOuterItCache = !forward_range<_View>;
+  using _OuterItCache = _If<_UseOuterItCache, __non_propagating_cache<iterator_t<_View>>, __empty_cache>;
+  _LIBCPP_NO_UNIQUE_ADDRESS _OuterItCache __outer_it_;
+
+  static constexpr bool _UseInnerCache = !is_reference_v<_InnerRng>;
+  using _InnerCache = _If<_UseInnerCache, __non_propagating_cache<remove_cvref_t<_InnerRng>>, __empty_cache>;
+  _LIBCPP_NO_UNIQUE_ADDRESS _InnerCache __inner_;
+
+  _LIBCPP_NO_UNIQUE_ADDRESS _Pattern __pattern_ = _Pattern();
+
+  template <bool _Const>
+  struct __iterator;
+
+  template <bool _Const>
+  struct __sentinel;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI join_with_view()
+    requires default_initializable<_View> && default_initializable<_Pattern>
+  = default;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit join_with_view(_View __base, _Pattern __pattern)
+      : __base_(std::move(__base)), __pattern_(std::move(__pattern)) {}
+
+  template <input_range _Range>
+    requires constructible_from<_View, views::all_t<_Range>> &&
+                 constructible_from<_Pattern, single_view<range_value_t<_InnerRng>>>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit join_with_view(_Range&& __r, range_value_t<_InnerRng> __e)
+      : __base_(views::all(std::forward<_Range>(__r))), __pattern_(views::single(std::move(__e))) {}
+
+  _LIBCPP_NODISCARD _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+    requires copy_constructible<_View>
+  {
+    return __base_;
+  }
+
+  _LIBCPP_NODISCARD _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+  _LIBCPP_NODISCARD _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
+    if constexpr (forward_range<_View>) {
+      constexpr bool __use_const = __simple_view<_View> && is_reference_v<_InnerRng> && __simple_view<_Pattern>;
+      return __iterator<__use_const>{*this, ranges::begin(__base_)};
+    } else {
+      __outer_it_.__emplace(ranges::begin(__base_));
+      return __iterator<false>{*this};
+    }
+  }
+
+  _LIBCPP_NODISCARD _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+    requires forward_range<const _View> && forward_range<const _Pattern> &&
+             is_reference_v<range_reference_t<const _View>> && input_range<range_reference_t<const _View>>
+  {
+    return __iterator<true>{*this, ranges::begin(__base_)};
+  }
+
+  _LIBCPP_NODISCARD _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
+    constexpr bool __use_const = __simple_view<_View> && __simple_view<_Pattern>;
+    if constexpr (forward_range<_View> && is_reference_v<_InnerRng> && forward_range<_InnerRng> &&
+                  common_range<_View> && common_range<_InnerRng>)
+      return __iterator<__use_const>{*this, ranges::end(__base_)};
+    else
+      return __sentinel<__use_const>{*this};
+  }
+
+  _LIBCPP_NODISCARD _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+    requires forward_range<const _View> && forward_range<const _Pattern> &&
+             is_reference_v<range_reference_t<const _View>> && input_range<range_reference_t<const _View>>
+  {
+    using _InnerConstRng = range_reference_t<const _View>;
+    if constexpr (forward_range<_InnerConstRng> && common_range<const _View> && common_range<_InnerConstRng>)
+      return __iterator<true>{*this, ranges::end(__base_)};
+    else
+      return __sentinel<true>{*this};
+  }
+};
+
+template <class _Range, class _Pattern>
+join_with_view(_Range&&, _Pattern&&) -> join_with_view<views::all_t<_Range>, views::all_t<_Pattern>>;
+
+template <input_range _Range>
+join_with_view(_Range&&, range_value_t<range_reference_t<_Range>>)
+    -> join_with_view<views::all_t<_Range>, single_view<range_value_t<range_reference_t<_Range>>>>;
+
+template <class _Base, class _PatternBase>
+struct __join_with_view_iterator_category {};
+
+template <class _Base, class _PatternBase>
+  requires is_reference_v<range_reference_t<_Base>> && forward_range<_Base> && forward_range<range_reference_t<_Base>>
+struct __join_with_view_iterator_category<_Base, _PatternBase> {
+private:
+  static consteval auto __get_iterator_category() noexcept {
+    using _OuterC   = iterator_traits<iterator_t<_Base>>::iterator_category;
+    using _InnerC   = iterator_traits<iterator_t<range_reference_t<_Base>>>::iterator_category;
+    using _PatternC = iterator_traits<iterator_t<_PatternBase>>::iterator_category;
+
+    if constexpr (!is_reference_v<common_reference_t<iter_reference_t<iterator_t<range_reference_t<_Base>>>,
+                                                     iter_reference_t<iterator_t<_PatternBase>>>>)
+      return input_iterator_tag{};
+    else if constexpr (derived_from<_OuterC, bidirectional_iterator_tag> &&
+                       derived_from<_InnerC, bidirectional_iterator_tag> &&
+                       derived_from<_PatternC, bidirectional_iterator_tag> && common_range<range_reference_t<_Base>> &&
+                       common_range<_PatternBase>)
+      return bidirectional_iterator_tag{};
+    else if constexpr (derived_from<_OuterC, forward_iterator_tag> && derived_from<_InnerC, forward_iterator_tag> &&
+                       derived_from<_PatternC, forward_iterator_tag>)
+      return forward_iterator_tag{};
+    else
+      return input_iterator_tag{};
+  }
+
+public:
+  using iterator_category = decltype(__get_iterator_category());
+};
+
+template <input_range _View, forward_range _Pattern>
+  requires view<_View> && input_range<range_reference_t<_View>> && view<_Pattern> &&
+           __compatible_joinable_ranges<range_reference_t<_View>, _Pattern>
+template <bool _Const>
+struct join_with_view<_View, _Pattern>::__iterator
+    : public __join_with_view_iterator_category<__maybe_const<_Const, _View>, __maybe_const<_Const, _Pattern>> {
+private:
+  friend join_with_view;
+
+  using _Parent      = __maybe_const<_Const, join_with_view>;
+  using _Base        = __maybe_const<_Const, _View>;
+  using _InnerBase   = range_reference_t<_Base>;
+  using _PatternBase = __maybe_const<_Const, _Pattern>;
+
+  using _OuterIter   = iterator_t<_Base>;
+  using _InnerIter   = iterator_t<_InnerBase>;
+  using _PatternIter = iterator_t<_PatternBase>;
+
+  static_assert(!_Const || forward_range<_Base>, "Const can only be true when Base models forward_range.");
+
+  static constexpr bool __ref_is_glvalue = is_reference_v<_InnerBase>;
+
+  _Parent* __parent_ = nullptr;
+
+  static constexpr bool _OuterIterPresent              = forward_range<_Base>;
+  using _OuterIterType                                 = _If<_OuterIterPresent, _OuterIter, std::__empty>;
+  _LIBCPP_NO_UNIQUE_ADDRESS _OuterIterType __outer_it_ = _OuterIterType();
+
+  variant<_PatternIter, _InnerIter> __inner_it_;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, _OuterIter __outer)
+    requires forward_range<_Base>
+      : __parent_(std::addressof(__parent)), __outer_it_(std::move(__outer)) {
+    if (__get_outer() != ranges::end(__parent_->__base_)) {
+      __inner_it_.template emplace<1>(ranges::begin(__update_inner()));
+      __satisfy();
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(_Parent& __parent)
+    requires(!forward_range<_Base>)
+      : __parent_(std::addressof(__parent)) {
+    if (__get_outer() != ranges::end(__parent_->__base_)) {
+      __inner_it_.template emplace<1>(ranges::begin(__update_inner()));
+      __satisfy();
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr _OuterIter& __get_outer() {
+    if constexpr (forward_range<_Base>)
+      return __outer_it_;
+    else
+      return *__parent_->__outer_it_;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr const _OuterIter& __get_outer() const {
+    if constexpr (forward_range<_Base>)
+      return __outer_it_;
+    else
+      return *__parent_->__outer_it_;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto& __update_inner() {
+    if constexpr (__ref_is_glvalue)
+      return std::__as_lvalue(*__get_outer());
+    else
+      return __parent_->__inner_.__emplace_from([&]() -> decltype(auto) { return *__get_outer(); });
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto& __get_inner() {
+    if constexpr (__ref_is_glvalue)
+      return std::__as_lvalue(*__get_outer());
+    else
+      return *__parent_->__inner_;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr void __satisfy() {
+    while (true) {
+      if (__inner_it_.index() == 0) {
+        if (std::get<0>(__inner_it_) != ranges::end(__parent_->__pattern_))
+          break;
+
+        __inner_it_.template emplace<1>(ranges::begin(__update_inner()));
+      } else {
+        if (std::get<1>(__inner_it_) != ranges::end(__get_inner()))
+          break;
+
+        if (++__get_outer() == ranges::end(__parent_->__base_)) {
+          if constexpr (__ref_is_glvalue)
+            __inner_it_.template emplace<0>();
+
+          break;
+        }
+
+        __inner_it_.template emplace<0>(ranges::begin(__parent_->__pattern_));
+      }
+    }
+  }
+
+  static consteval auto __get_iterator_concept() noexcept {
+    if constexpr (__ref_is_glvalue && bidirectional_range<_Base> && __bidirectional_common<_InnerBase> &&
+                  __bidirectional_common<_PatternBase>)
+      return bidirectional_iterator_tag{};
+    else if constexpr (__ref_is_glvalue && forward_range<_Base> && forward_range<_InnerBase>)
+      return forward_iterator_tag{};
+    else
+      return input_iterator_tag{};
+  }
+
+public:
+  using iterator_concept = decltype(__get_iterator_concept());
+  using value_type       = common_type_t<iter_value_t<_InnerIter>, iter_value_t<_PatternIter>>;
+  using difference_type =
+      common_type_t<iter_difference_t<_OuterIter>, iter_difference_t<_InnerIter>, iter_difference_t<_PatternIter>>;
+
+  _LIBCPP_HIDE_FROM_ABI __iterator() = default;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
+    requires _Const && convertible_to<iterator_t<_View>, _OuterIter> &&
+                 convertible_to<iterator_t<_InnerRng>, _InnerIter> && convertible_to<iterator_t<_Pattern>, _PatternIter>
+      : __parent_(__i.__parent_), __outer_it_(std::move(__i.__outer_it_)) {
+    if (__i.__inner_it_.index() == 0) {
+      __inner_it_.template emplace<0>(std::get<0>(std::move(__i.__inner_it_)));
+    } else {
+      __inner_it_.template emplace<1>(std::get<1>(std::move(__i.__inner_it_)));
+    }
+  }
+
+  _LIBCPP_NODISCARD _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const {
+    using __reference = common_reference_t<iter_reference_t<_InnerIter>, iter_reference_t<_PatternIter>>;
+    return std::visit([](auto& __it) -> __reference { return *__it; }, __inner_it_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() {
+    std::visit([](auto& __it) { ++__it; }, __inner_it_);
+    __satisfy();
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { ++*this; }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator++(int)
+    requires __ref_is_glvalue && forward_iterator<_OuterIter> && forward_iterator<_InnerIter>
+  {
+    __iterator __tmp = *this;
+    ++*this;
+    return __tmp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator--()
+    requires __ref_is_glvalue
+          && bidirectional_range<_Base> && __bidirectional_common<_InnerBase> && __bidirectional_common<_PatternBase>
+  {
+    if (__outer_it_ == ranges::end(__parent_->__base_)) {
+      auto&& __inner = *--__outer_it_;
+      __inner_it_.template emplace<1>(ranges::end(__inner));
+    }
+
+    while (true) {
+      if (__inner_it_.index() == 0) {
+        auto& __it = std::get<0>(__inner_it_);
+        if (__it == ranges::begin(__parent_->__pattern_)) {
+          auto&& __inner = *--__outer_it_;
+          __inner_it_.template emplace<1>(ranges::end(__inner));
+        } else
+          break;
+      } else {
+        auto& __it     = std::get<1>(__inner_it_);
+        auto&& __inner = *__outer_it_;
+        if (__it == ranges::begin(__inner))
+          __inner_it_.template emplace<0>(ranges::end(__parent_->__pattern_));
+        else
+          break;
+      }
+    }
+
+    std::visit([](auto& __it) { --__it; }, __inner_it_);
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int)
+    requires __ref_is_glvalue
+          && bidirectional_range<_Base> && __bidirectional_common<_InnerBase> && __bidirectional_common<_PatternBase>
+  {
+    __iterator __tmp = *this;
+    --*this;
+    return __tmp;
+  }
+
+  _LIBCPP_NODISCARD _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __iterator& __y)
+    requires __ref_is_glvalue && forward_range<_Base> && equality_comparable<_InnerIter>
+  {
+    return __x.__outer_it_ == __y.__outer_it_ && __x.__inner_it_ == __y.__inner_it_;
+  }
+
+  _LIBCPP_NODISCARD _LIBCPP_HIDE_FROM_ABI friend constexpr decltype(auto) iter_move(const __iterator& __x) {
+    using __rvalue_reference =
+        common_reference_t<iter_rvalue_reference_t<_InnerIter>, iter_rvalue_reference_t<_PatternIter>>;
+    return std::visit<__rvalue_reference>(ranges::iter_move, __x.__inner_it_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr void iter_swap(const __iterator& __x, const __iterator& __y)
+    requires indirectly_swappable<_InnerIter, _PatternIter>
+  {
+    std::visit(ranges::iter_swap, __x.__inner_it_, __y.__inner_it_);
+  }
+};
+
+template <input_range _View, forward_range _Pattern>
+  requires view<_View> && input_range<range_reference_t<_View>> && view<_Pattern> &&
+           __compatible_joinable_ranges<range_reference_t<_View>, _Pattern>
+template <bool _Const>
+struct join_with_view<_View, _Pattern>::__sentinel {
+private:
+  friend join_with_view;
+
+  using _Parent = __maybe_const<_Const, join_with_view>;
+  using _Base   = __maybe_const<_Const, _View>;
+
+  sentinel_t<_Base> __end_ = sentinel_t<_Base>();
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_Parent& __parent) : __end_(ranges::end(__parent.__base_)) {}
+
+public:
+  _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
+    requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
+      : __end_(std::move(__s.__end_)) {}
+
+  template <bool _OtherConst>
+    requires sentinel_for<sentinel_t<_Base>, iterator_t<__maybe_const<_OtherConst, _View>>>
+  _LIBCPP_NODISCARD _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+  operator==(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
+    return __x.__get_outer() == __y.__end_;
+  }
+};
+
+namespace views {
+namespace __join_with_view {
+struct __fn {
+  template <class _Range, class _Pattern>
+  _LIBCPP_NODISCARD _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Range&& __range, _Pattern&& __pattern) const
+      noexcept(noexcept(/**/ join_with_view(std::forward<_Range>(__range), std::forward<_Pattern>(__pattern))))
+          -> decltype(/*--*/ join_with_view(std::forward<_Range>(__range), std::forward<_Pattern>(__pattern))) {
+    return /*-------------*/ join_with_view(std::forward<_Range>(__range), std::forward<_Pattern>(__pattern));
+  }
+
+  template <class _Pattern>
+    requires constructible_from<decay_t<_Pattern>, _Pattern>
+  _LIBCPP_NODISCARD _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Pattern&& __pattern) const
+      noexcept(is_nothrow_constructible_v<decay_t<_Pattern>, _Pattern>) {
+    return __range_adaptor_closure_t(std::__bind_back(*this, std::forward<_Pattern>(__pattern)));
+  }
+};
+} // namespace __join_with_view
+
+inline namespace __cpo {
+inline constexpr auto join_with = __join_with_view::__fn{};
+} // namespace __cpo
+} // namespace views
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___RANGES_JOIN_WITH_VIEW_H
diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index 9ffccf66ff0948..ddec16d3a26b7c 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -1726,6 +1726,10 @@ module std_private_ranges_join_view                  [system] {
   export std_private_iterator_iterator_with_data
   export std_private_iterator_segmented_iterator
 }
+module std_private_ranges_join_with_view             [system] {
+  header "__ranges/join_with_view.h"
+  export std_variant
+}
 module std_private_ranges_lazy_split_view            [system] {
   header "__ranges/lazy_split_view.h"
   export std_private_ranges_non_propagating_cache
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 07a525ed8641fd..8a2dbf1d117779 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -285,6 +285,18 @@ namespace std::ranges {
     requires view<V> && input_range<range_reference_t<V>>
   class join_view;
 
+  // [range.join.with], join with view
+  template<class R, class P>
+    concept compatible-joinable-ranges = see below; // exposition only
+
+  template<input_range V, forward_range Pattern>
+    requires view<V> && input_range<range_reference_t<V>>
+          && view<Pattern>
+          && compatible-joinable-ranges<range_reference_t<V>, Pattern>
+  class join_with_view;                                                     // since C++23
+
+  namespace views { inline constexpr unspecified join_with = unspecified; } // since C++23
+
   // [range.lazy.split], lazy split view
   template<class R>
     concept tiny-range = see below;   // exposition only
@@ -401,6 +413,7 @@ namespace std {
 #include <__ranges/from_range.h>
 #include <__ranges/iota_view.h>
 #include <__ranges/join_view.h>
+#include <__ranges/join_with_view.h>
 #include <__ranges/lazy_split_view.h>
 #include <__ranges/rbegin.h>
 #include <__ranges/ref_view.h>
diff --git a/libcxx/include/version b/libcxx/include/version
index cac6eaa3b6e880..4925cf2ae9ed6f 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -477,7 +477,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # define __cpp_lib_ranges_chunk_by                      202202L
 # define __cpp_lib_ranges_contains                      202207L
 // # define __cpp_lib_ranges_iota                          202202L
-// # define __cpp_lib_ranges_join_with                     202202L
+# define __cpp_lib_ranges_join_with                     202202L
 # define __cpp_lib_ranges_repeat                        202207L
 // # define __cpp_lib_ranges_slide                         202202L
 # define __cpp_lib_ranges_starts_ends_with              202106L
diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc
index f71efe948ede10..eebb2a450c15d1 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -223,13 +223,16 @@ export namespace std {
     namespace views {
       using std::ranges::views::join;
     } // namespace views
-#if 0
+
+#if _LIBCPP_STD_VER >= 23
+    // [range.join.with]
     using std::ranges::join_with_view;
 
     namespace views {
       using std::ranges::views::join_with;
     } // namespace views
-#endif
+#endif // _LIBCPP_STD_VER >= 23
+
     using std::ranges::lazy_split_view;
 
     // [range.split], split view
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/deref.nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/deref.nodiscard.verify.cpp
new file mode 100644
index 00000000000000..58abf717d7bbeb
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/deref.nodiscard.verify.cpp
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <ranges>
+
+// Test the libc++ extension that std::ranges::join_with_view::iterator<Const>::operator* is marked as [[nodiscard]].
+
+#include <ranges>
+#include <utility>
+
+void test() {
+  char range[3][2] = {{'x', 'x'}, {'y', 'y'}, {'z', 'z'}};
+  char pattern[2]  = {',', ' '};
+
+  std::ranges::join_with_view view(range, pattern);
+
+  // clang-format off
+  *view.begin(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *std::as_const(view).begin(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  // clang-format on
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/eq.nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/eq.nodiscard.verify.cpp
new file mode 100644
index 00000000000000..78b6332f778e13
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/eq.nodiscard.verify.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <ranges>
+
+// Test the libc++ extension that std::ranges::join_with_view::iterator<Const>::operator== is marked as [[nodiscard]].
+
+#include <ranges>
+#include <utility>
+
+void test() {
+  char16_t range[3][1] = {{u'x'}, {u'y'}, {u'z'}};
+  char16_t pattern[1]  = {u'-'};
+
+  std::ranges::join_with_view view(range, pattern);
+
+  // clang-format off
+  (view.begin() == view.end()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (std::as_const(view).begin() == view.end()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (view.begin() == std::as_const(view).end()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (std::as_const(view).begin() == std::as_const(view).end()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  // clang-format on
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_move.nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_move.nodiscard.verify.cpp
new file mode 100644
index 00000000000000..1e4a92f827572f
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_move.nodiscard.verify.cpp
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <ranges>
+
+// Test the libc++ extension that std::ranges::join_with_view::iterator<Const>::iter_move is marked as [[nodiscard]].
+
+#include <ranges>
+#include <utility>
+
+void test() {
+  long range[2][1] = {{0L}, {2L}};
+  long pattern[1]  = {1L};
+
+  std::ranges::join_with_view view(range, pattern);
+
+  // clang-format off
+  iter_move(view.begin()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  iter_move(std::as_const(view).begin()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  // clang-format on
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/no_unique_address.compile.pass.cpp
new file mode 100644
index 00000000000000..05074cbb313a0b
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/no_unique_address.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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// XFAIL: msvc && clang-17
+
+// <ranges>
+
+// This test ensures that we use `[[no_unique_address]]` in `join_with_view::iterator`.
+
+#include <ranges>
+
+#include "test_iterators.h"
+#include "test_range.h"
+
+struct InputView : std::ranges::view_base {
+  using Inner = test_range<forward_iterator>;
+
+  cpp20_input_iterator<Inner*> begin();
+  sentinel_wrapper<cpp20_input_iterator<Inner*>> end();
+};
+
+static_assert(std::ranges::input_range<InputView>);
+static_assert(!std::ranges::forward_range<InputView>);
+
+struct Pattern : std::ranges::view_base {
+  int* begin();
+  int* end();
+};
+
+static_assert(alignof(void*) == alignof(std::variant<int*, int*>)); // alignof(__parent_) == alignof(__inner_it_)
+static_assert(sizeof(std::ranges::iterator_t<std::ranges::join_with_view<InputView, Pattern>>) ==
+              sizeof(void*) + sizeof(std::variant<int*, int*>)); // sizeof(__parent_) + sizeof(__inner_it_)
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.overview/adaptor.nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.overview/adaptor.nodiscard.verify.cpp
new file mode 100644
index 00000000000000..bd2972f938648c
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.overview/adaptor.nodiscard.verify.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <ranges>
+
+// Test the libc++ extension that std::views::join_with is marked as [[nodiscard]].
+
+#include <ranges>
+
+void test() {
+  int range[3][3]     = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
+  int pattern_base[2] = {-1, -1};
+  auto pattern        = std::views::all(pattern_base);
+
+  // clang-format off
+  std::views::join_with(pattern); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::join_with(range, pattern); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  range | std::views::join_with(pattern); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::reverse | std::views::join_with(pattern); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+
+  std::views::join_with(0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::join_with(range, 0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  range | std::views::join_with(0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::reverse | std::views::join_with(0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  // clang-format on
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.sentinel/eq.nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.sentinel/eq.nodiscard.verify.cpp
new file mode 100644
index 00000000000000..8cd234b85ad1d0
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.sentinel/eq.nodiscard.verify.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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <ranges>
+
+// Test the libc++ extension that std::ranges::join_with_view::sentinel<Const>::operator== is marked as [[nodiscard]].
+
+#include <array>
+#include <ranges>
+#include <utility>
+
+#include "test_iterators.h"
+#include "test_range.h"
+
+void test() {
+  std::array<test_range<cpp20_input_iterator>, 0> range;
+  std::array<int, 0> pattern;
+
+  std::ranges::join_with_view view(range, pattern);
+  static_assert(!std::ranges::common_range<decltype(view)>);
+
+  // clang-format off
+  (view.begin() == view.end()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (std::as_const(view).begin() == view.end()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (view.begin() == std::as_const(view).end()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  (std::as_const(view).begin() == std::as_const(view).end()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  // clang-format on
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/base.nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/base.nodiscard.verify.cpp
new file mode 100644
index 00000000000000..b08b9f9b8bc0da
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/base.nodiscard.verify.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <ranges>
+
+// Test the libc++ extension that std::ranges::join_with_view::base is marked as [[nodiscard]].
+
+#include <ranges>
+#include <utility>
+
+void test() {
+  int range[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
+  int pattern[2]  = {-1, -1};
+
+  std::ranges::join_with_view view(range, pattern);
+
+  // clang-format off
+  view.base(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(view).base(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(std::as_const(view)).base(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(view).base(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  // clang-format on
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/begin.nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/begin.nodiscard.verify.cpp
new file mode 100644
index 00000000000000..6d930d46be77ac
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/begin.nodiscard.verify.cpp
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <ranges>
+
+// Test the libc++ extension that std::ranges::join_with_view::begin is marked as [[nodiscard]].
+
+#include <ranges>
+#include <utility>
+
+void test() {
+  int range[3][2] = {{1, 3}, {4, 6}, {7, 9}};
+  int pattern[1]  = {-2};
+
+  std::ranges::join_with_view view(range, pattern);
+
+  // clang-format off
+  view.begin(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(view).begin(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  // clang-format on
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/end.nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/end.nodiscard.verify.cpp
new file mode 100644
index 00000000000000..1d5f0e394d67f0
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/end.nodiscard.verify.cpp
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <ranges>
+
+// Test the libc++ extension that std::ranges::join_with_view::end is marked as [[nodiscard]].
+
+#include <ranges>
+#include <utility>
+
+void test() {
+  int range[3][2] = {{1, 2}, {4, 5}, {7, 8}};
+  int pattern[1]  = {-3};
+
+  std::ranges::join_with_view view(range, pattern);
+
+  // clang-format off
+  view.end(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(view).end(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  // clang-format on
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/no_unique_address.compile.pass.cpp
new file mode 100644
index 00000000000000..495a453a898089
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/no_unique_address.compile.pass.cpp
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// XFAIL: msvc
+
+// <ranges>
+
+// This test ensures that we use `[[no_unique_address]]` in `join_with_view`.
+
+#include <ranges>
+#include <string>
+
+struct ForwardView : std::ranges::view_base {
+  std::string* begin() const;
+  std::string* end() const;
+};
+
+static_assert(std::ranges::forward_range<ForwardView>);
+static_assert(std::is_reference_v<std::ranges::range_reference_t<ForwardView>>);
+
+struct Pattern : std::ranges::view_base {
+  char* begin() const;
+  char* end() const;
+};
+
+template <class View>
+struct Test {
+  [[no_unique_address]] View view;
+  char c;
+};
+
+static_assert(sizeof(Test<std::ranges::join_with_view<ForwardView, Pattern>>) ==
+              sizeof(std::ranges::join_with_view<ForwardView, Pattern>));
diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index c720a0c9a05ec7..78f0859321cec1 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -666,6 +666,7 @@ ranges optional
 ranges span
 ranges tuple
 ranges type_traits
+ranges variant
 ranges version
 ratio climits
 ratio cstdint
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index b00436a1be7f7c..0f290762a96680 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -671,6 +671,7 @@ ranges optional
 ranges span
 ranges tuple
 ranges type_traits
+ranges variant
 ranges version
 ratio climits
 ratio cstdint
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index 4b8c12929de0d6..5aebe38c2d481c 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -674,6 +674,7 @@ ranges optional
 ranges span
 ranges tuple
 ranges type_traits
+ranges variant
 ranges version
 ratio climits
 ratio cstdint
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index a51a7e2de1d380..2a8a67ce5028a6 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -675,6 +675,7 @@ ranges optional
 ranges span
 ranges tuple
 ranges type_traits
+ranges variant
 ranges version
 ratio climits
 ratio cstdint
diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv
index 5280d75cbf2e1f..c4cf997c69208e 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -682,6 +682,7 @@ ranges optional
 ranges span
 ranges tuple
 ranges type_traits
+ranges variant
 ranges version
 ratio climits
 ratio cstdint
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
index 30feacd796d8e1..0704a87bb9ad07 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
@@ -293,17 +293,11 @@
 #   error "__cpp_lib_ranges_concat should not be defined before c++26"
 # endif
 
-# if !defined(_LIBCPP_VERSION)
-#   ifndef __cpp_lib_ranges_join_with
-#     error "__cpp_lib_ranges_join_with should be defined in c++23"
-#   endif
-#   if __cpp_lib_ranges_join_with != 202202L
-#     error "__cpp_lib_ranges_join_with should have the value 202202L in c++23"
-#   endif
-# else // _LIBCPP_VERSION
-#   ifdef __cpp_lib_ranges_join_with
-#     error "__cpp_lib_ranges_join_with should not be defined because it is unimplemented in libc++!"
-#   endif
+# ifndef __cpp_lib_ranges_join_with
+#   error "__cpp_lib_ranges_join_with should be defined in c++23"
+# endif
+# if __cpp_lib_ranges_join_with != 202202L
+#   error "__cpp_lib_ranges_join_with should have the value 202202L in c++23"
 # endif
 
 # ifndef __cpp_lib_ranges_repeat
@@ -421,17 +415,11 @@
 #   endif
 # endif
 
-# if !defined(_LIBCPP_VERSION)
-#   ifndef __cpp_lib_ranges_join_with
-#     error "__cpp_lib_ranges_join_with should be defined in c++26"
-#   endif
-#   if __cpp_lib_ranges_join_with != 202202L
-#     error "__cpp_lib_ranges_join_with should have the value 202202L in c++26"
-#   endif
-# else // _LIBCPP_VERSION
-#   ifdef __cpp_lib_ranges_join_with
-#     error "__cpp_lib_ranges_join_with should not be defined because it is unimplemented in libc++!"
-#   endif
+# ifndef __cpp_lib_ranges_join_with
+#   error "__cpp_lib_ranges_join_with should be defined in c++26"
+# endif
+# if __cpp_lib_ranges_join_with != 202202L
+#   error "__cpp_lib_ranges_join_with should have the value 202202L in c++26"
 # endif
 
 # ifndef __cpp_lib_ranges_repeat
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 32ed30e21cbbe3..8ba7ca5e6027c7 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
@@ -5576,17 +5576,11 @@
 #   endif
 # endif
 
-# if !defined(_LIBCPP_VERSION)
-#   ifndef __cpp_lib_ranges_join_with
-#     error "__cpp_lib_ranges_join_with should be defined in c++23"
-#   endif
-#   if __cpp_lib_ranges_join_with != 202202L
-#     error "__cpp_lib_ranges_join_with should have the value 202202L in c++23"
-#   endif
-# else // _LIBCPP_VERSION
-#   ifdef __cpp_lib_ranges_join_with
-#     error "__cpp_lib_ranges_join_with should not be defined because it is unimplemented in libc++!"
-#   endif
+# ifndef __cpp_lib_ranges_join_with
+#   error "__cpp_lib_ranges_join_with should be defined in c++23"
+# endif
+# if __cpp_lib_ranges_join_with != 202202L
+#   error "__cpp_lib_ranges_join_with should have the value 202202L in c++23"
 # endif
 
 # ifndef __cpp_lib_ranges_repeat
@@ -7378,17 +7372,11 @@
 #   endif
 # endif
 
-# if !defined(_LIBCPP_VERSION)
-#   ifndef __cpp_lib_ranges_join_with
-#     error "__cpp_lib_ranges_join_with should be defined in c++26"
-#   endif
-#   if __cpp_lib_ranges_join_with != 202202L
-#     error "__cpp_lib_ranges_join_with should have the value 202202L in c++26"
-#   endif
-# else // _LIBCPP_VERSION
-#   ifdef __cpp_lib_ranges_join_with
-#     error "__cpp_lib_ranges_join_with should not be defined because it is unimplemented in libc++!"
-#   endif
+# ifndef __cpp_lib_ranges_join_with
+#   error "__cpp_lib_ranges_join_with should be defined in c++26"
+# endif
+# if __cpp_lib_ranges_join_with != 202202L
+#   error "__cpp_lib_ranges_join_with should have the value 202202L in c++26"
 # endif
 
 # ifndef __cpp_lib_ranges_repeat
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/ctor.default.pass.cpp
new file mode 100644
index 00000000000000..dfd1d9ce82f0c2
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/ctor.default.pass.cpp
@@ -0,0 +1,77 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <ranges>
+
+// iterator() = default;
+
+#include <ranges>
+
+#include <cassert>
+#include <initializer_list>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+
+#include "../types.h"
+#include "test_comparisons.h"
+#include "test_iterators.h"
+
+constexpr bool test() {
+  { // `V` and `Pattern` model forward range
+    using Inner     = BasicVectorView<int, ViewProperties{}, forward_iterator>;
+    using V         = BasicVectorView<Inner, ViewProperties{}, forward_iterator>;
+    using Pattern   = Inner;
+    using JWV       = std::ranges::join_with_view<V, Pattern>;
+    using Iter      = std::ranges::iterator_t<JWV>;
+    using ConstIter = std::ranges::iterator_t<const JWV>;
+
+    Iter iter;
+    assert(testEquality(iter, Iter{}, true));
+
+    ConstIter citer;
+    assert(testEquality(citer, ConstIter{}, true));
+    assert(testEquality(iter, citer, true));
+
+    std::ranges::join_with_view<V, Pattern> jwv(V{Inner{1, 2}, Inner{2, 1}}, Pattern{3, 3});
+    Iter jwv_iter       = jwv.begin();
+    ConstIter jwv_citer = std::as_const(jwv).begin();
+    assert(testEquality(jwv_iter, jwv_citer, true));
+
+    assert(testEquality(jwv_iter, iter, false));
+    assert(testEquality(jwv_iter, citer, false));
+    assert(testEquality(jwv_citer, iter, false));
+    assert(testEquality(jwv_citer, citer, false));
+  }
+
+  { // `InnerIter` is not default constructible (does not model forward iterator, JWV cannot be const-accessed)
+    using Inner   = BasicVectorView<char, ViewProperties{.common = false}, EqComparableInputIter>;
+    using V       = BasicVectorView<Inner, ViewProperties{.common = false}, forward_iterator>;
+    using Pattern = BasicVectorView<char, ViewProperties{}, forward_iterator>;
+    using JWV     = std::ranges::join_with_view<V, Pattern>;
+    using Iter    = std::ranges::iterator_t<JWV>;
+
+    Iter iter;
+    assert(testEquality(iter, Iter{}, true));
+
+    std::ranges::join_with_view<V, Pattern> jwv(V{Inner{'a', 'b'}, Inner{'c', 'd'}}, Pattern{',', ' '});
+    Iter jwv_iter = jwv.begin();
+    assert(testEquality(jwv_iter, iter, false));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/ctor.not_const.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/ctor.not_const.pass.cpp
new file mode 100644
index 00000000000000..16d31618c93dc8
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/ctor.not_const.pass.cpp
@@ -0,0 +1,104 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <ranges>
+
+// constexpr iterator(iterator<!Const> i)
+//   requires Const && convertible_to<iterator_t<V>, OuterIter> &&
+//            convertible_to<iterator_t<InnerRng>, InnerIter> &&
+//            convertible_to<iterator_t<Pattern>, PatternIter>;
+
+#include <ranges>
+
+#include <cassert>
+#include <vector>
+
+#include "../types.h"
+
+constexpr bool test() {
+  { // Regular conversion from `!Const` to `Const` iterator
+    std::vector<std::vector<int>> vec = {{1, 2}, {3, 4}, {5, 6}};
+    int pattern                       = 0;
+    std::ranges::join_with_view jwv(vec, pattern);
+    auto it = jwv.begin();
+    assert(*it == 1);
+
+    using CIter      = std::ranges::iterator_t<const decltype(jwv)>;
+    const CIter cit1 = it; // `cit1` points to element of `V`
+    assert(*cit1 == 1);
+    assert(cit1 == it);
+
+    std::ranges::advance(it, 2);
+    assert(*it == 0);
+    CIter cit2 = it; // `cit2` points to element of `Pattern`
+    assert(*cit2 == 0);
+    assert(cit2 == it);
+
+    ++it;
+    assert(*it == 3);
+    CIter cit3 = it;
+    assert(*cit3 == 3);
+    assert(cit3 == it);
+
+    --cit3;
+    assert(cit2 == cit3);
+  }
+
+  { // Test conversion from `Const` to `!Const` (should be invalid)
+    using V       = std::vector<std::vector<int>>;
+    using Pattern = std::ranges::single_view<int>;
+    using JWV     = std::ranges::join_with_view<std::views::all_t<V>, Pattern>;
+    using Iter    = std::ranges::iterator_t<JWV>;
+    using CIter   = std::ranges::iterator_t<const JWV>;
+    static_assert(!std::convertible_to<CIter, Iter>);
+    static_assert(!std::constructible_from<Iter, CIter>);
+  }
+
+  { // When `convertible_to<iterator_t<V>, OuterIter>` is not modeled
+    using Inner   = std::vector<short>;
+    using V       = ConstOppositeView<Inner>;
+    using Pattern = std::ranges::single_view<short>;
+    using JWV     = std::ranges::join_with_view<V, Pattern>;
+    using Iter    = std::ranges::iterator_t<JWV>;
+    using CIter   = std::ranges::iterator_t<const JWV>;
+    static_assert(!std::convertible_to<CIter, Iter>);
+    static_assert(!std::constructible_from<Iter, CIter>);
+  }
+
+  { // When `convertible_to<iterator_t<InnerRng>, InnerIter>` is not modeled
+    using Inner   = ConstOppositeView<long>;
+    using V       = std::vector<Inner>;
+    using Pattern = std::ranges::single_view<long>;
+    using JWV     = std::ranges::join_with_view<std::views::all_t<V>, Pattern>;
+    using Iter    = std::ranges::iterator_t<JWV>;
+    using CIter   = std::ranges::iterator_t<const JWV>;
+    static_assert(!std::convertible_to<CIter, Iter>);
+    static_assert(!std::constructible_from<Iter, CIter>);
+  }
+
+  { // When `convertible_to<iterator_t<Pattern>, PatternIter>` is not modeled
+    using V       = std::vector<std::vector<long long>>;
+    using Pattern = ConstOppositeView<long long>;
+    using JWV     = std::ranges::join_with_view<std::views::all_t<V>, Pattern>;
+    using Iter    = std::ranges::iterator_t<JWV>;
+    using CIter   = std::ranges::iterator_t<const JWV>;
+    static_assert(!std::convertible_to<CIter, Iter>);
+    static_assert(!std::constructible_from<Iter, CIter>);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/decrement.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/decrement.pass.cpp
new file mode 100644
index 00000000000000..8bb6d71917421d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/decrement.pass.cpp
@@ -0,0 +1,283 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <ranges>
+
+// constexpr iterator& operator--()
+//   requires ref-is-glvalue && bidirectional_range<Base> &&
+//            bidirectional-common<InnerBase> && bidirectional-common<PatternBase>;
+// constexpr iterator operator--(int)
+//   requires ref-is-glvalue && bidirectional_range<Base> &&
+//            bidirectional-common<InnerBase> && bidirectional-common<PatternBase>;
+
+#include <ranges>
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <forward_list>
+#include <string>
+#include <string_view>
+#include <vector>
+
+#include "../types.h"
+
+template <class I>
+concept CanPreDecrement = requires(I& i) {
+  { --i } -> std::same_as<I&>;
+};
+
+template <class I>
+concept CanPostDecrement = requires(I& i) {
+  { i-- } -> std::same_as<I>;
+};
+
+template <class I>
+concept CanDecrement = CanPreDecrement<I> && CanPostDecrement<I>;
+
+constexpr bool test() {
+  { // `V` and `Pattern` are not empty. Test return type too.
+    using V       = std::ranges::owning_view<std::vector<std::string>>;
+    using Pattern = std::ranges::single_view<char>;
+    using JWV     = std::ranges::join_with_view<V, Pattern>;
+
+    using Iter  = std::ranges::iterator_t<JWV>;
+    using CIter = std::ranges::iterator_t<const JWV>;
+    static_assert(CanDecrement<Iter>);
+    static_assert(CanDecrement<CIter>);
+
+    JWV jwv(V{{"01", "23", "45"}}, Pattern{'_'});
+
+    {
+      auto it                                   = jwv.end();
+      std::same_as<Iter&> decltype(auto) it_ref = --it;
+      assert(it_ref == it);
+      assert(*it == '5');
+      std::same_as<Iter> decltype(auto) it_copy = it--;
+      assert(--it_copy == it);
+      --it;
+      assert(*it == '_');
+      it--;
+      assert(*it == '3');
+      --it;
+      it--;
+      assert(*it == '_');
+    }
+
+    {
+      auto cit                                    = std::as_const(jwv).end();
+      std::same_as<CIter&> decltype(auto) cit_ref = --cit;
+      assert(cit_ref == cit);
+      assert(*cit == '5');
+      std::same_as<CIter> decltype(auto) cit_copy = cit--;
+      assert(--cit_copy == cit);
+      --cit;
+      assert(*cit == '_');
+      cit--;
+      assert(*cit == '3');
+      --cit;
+      cit--;
+      assert(*cit == '_');
+    }
+
+    assert(std::ranges::equal(std::views::reverse(std::move(jwv)), std::string_view{"54_32_10"}));
+  }
+
+  { // `Pattern` is empty, `V` is not.
+    using Inner   = std::array<int, 1>;
+    using V       = std::ranges::owning_view<std::array<Inner, 3>>;
+    using Pattern = std::ranges::owning_view<std::array<int, 0>>;
+    using JWV     = std::ranges::join_with_view<V, Pattern>;
+
+    JWV jwv(V{{Inner{-9}, Inner{-99}, Inner{-999}}}, Pattern{});
+
+    {
+      auto it = jwv.end();
+      --it;
+      assert(*it == -999);
+      it--;
+      assert(*it == -99);
+      --it;
+      assert(*it == -9);
+      assert(it == jwv.begin());
+    }
+
+    {
+      auto cit = std::as_const(jwv).end();
+      --cit;
+      assert(*cit == -999);
+      cit--;
+      assert(*cit == -99);
+      --cit;
+      assert(*cit == -9);
+      assert(cit == std::as_const(jwv).begin());
+    }
+  }
+
+#if !defined(TEST_COMPILER_GCC) // GCC c++/101777
+  { // `V` has empty subrange in the middle, `Pattern` is not empty. Try to go back and forth.
+    using V       = std::array<std::vector<int>, 3>;
+    using Pattern = std::ranges::single_view<int>;
+    using JWV     = std::ranges::join_with_view<std::ranges::owning_view<V>, Pattern>;
+
+    JWV jwv(V{{{5}, {}, {125}}}, Pattern{1});
+
+    {
+      auto it = jwv.end();
+      --it;
+      assert(*it == 125);
+      it--;
+      assert(*it == 1);
+      --it;
+      assert(*it == 1);
+      it--;
+      assert(*it == 5);
+      ++it;
+      assert(*it == 1);
+      --it;
+      assert(*it == 5);
+      std::ranges::advance(it, 4);
+      it--;
+      assert(*it == 125);
+    }
+
+    {
+      auto cit = std::as_const(jwv).end();
+      --cit;
+      assert(*cit == 125);
+      cit--;
+      assert(*cit == 1);
+      --cit;
+      assert(*cit == 1);
+      cit--;
+      assert(*cit == 5);
+      ++cit;
+      assert(*cit == 1);
+      --cit;
+      assert(*cit == 5);
+      std::ranges::advance(cit, 4);
+      cit--;
+      assert(*cit == 125);
+    }
+  }
+
+  { // Only first element of `V` is not empty. `Pattern` is empty. Try to go back and forth.
+    using Inner   = std::vector<int>;
+    using V       = std::ranges::owning_view<std::array<Inner, 3>>;
+    using Pattern = std::ranges::empty_view<int>;
+    using JWV     = std::ranges::join_with_view<V, Pattern>;
+
+    JWV jwv(V{{Inner{999}, {}, {}}}, Pattern{});
+
+    {
+      auto it = jwv.end();
+      --it;
+      assert(*it == 999);
+      ++it;
+      assert(it == jwv.end());
+      it--;
+      assert(*it == 999);
+    }
+
+    {
+      auto cit = std::as_const(jwv).end();
+      --cit;
+      assert(*cit == 999);
+      ++cit;
+      assert(cit == std::as_const(jwv).end());
+      cit--;
+      assert(*cit == 999);
+    }
+  }
+#endif // !defined(TEST_COMPILER_GCC)
+
+  { // `ref-is-glvalue` is false
+    using V       = RvalueVector<std::vector<int>>;
+    using Pattern = std::ranges::empty_view<int>;
+    using JWV     = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;
+    using Iter    = std::ranges::iterator_t<JWV>;
+    static_assert(!CanPreDecrement<Iter>);
+    static_assert(!CanPostDecrement<Iter>);
+  }
+
+  { // `Base` does not model bidirectional range
+    using V       = std::ranges::owning_view<std::forward_list<std::vector<int>>>;
+    using Pattern = std::ranges::single_view<int>;
+    using JWV     = std::ranges::join_with_view<V, Pattern>;
+    using Iter    = std::ranges::iterator_t<JWV>;
+    using CIter   = std::ranges::iterator_t<const JWV>;
+    static_assert(!CanPreDecrement<Iter>);
+    static_assert(!CanPostDecrement<Iter>);
+    static_assert(!CanPreDecrement<CIter>);
+    static_assert(!CanPostDecrement<CIter>);
+  }
+
+  {   // InnerBase does not model bidirectional-common
+    { // InnerBase does not model bidirectional range
+      using V       = std::ranges::owning_view<std::vector<std::forward_list<int>>>;
+      using Pattern = std::ranges::single_view<int>;
+      using JWV     = std::ranges::join_with_view<V, Pattern>;
+      using Iter    = std::ranges::iterator_t<JWV>;
+      using CIter   = std::ranges::iterator_t<const JWV>;
+      static_assert(!CanPreDecrement<Iter>);
+      static_assert(!CanPostDecrement<Iter>);
+      static_assert(!CanPreDecrement<CIter>);
+      static_assert(!CanPostDecrement<CIter>);
+    }
+
+    { // InnerBase does not model common range
+      using InnerBase = BasicVectorView<int, ViewProperties{.common = false}, bidirectional_iterator>;
+      using V         = std::ranges::owning_view<std::vector<InnerBase>>;
+      using Pattern   = std::ranges::single_view<int>;
+      using JWV       = std::ranges::join_with_view<V, Pattern>;
+      using Iter      = std::ranges::iterator_t<JWV>;
+      using CIter     = std::ranges::iterator_t<const JWV>;
+      static_assert(!CanPreDecrement<Iter>);
+      static_assert(!CanPostDecrement<Iter>);
+      static_assert(!CanPreDecrement<CIter>);
+      static_assert(!CanPostDecrement<CIter>);
+    }
+  }
+
+  {   // PatternBase does not model bidirectional-common
+    { // PatternBase does not model bidirectional range
+      using V       = std::ranges::owning_view<std::vector<std::vector<int>>>;
+      using Pattern = std::ranges::owning_view<std::forward_list<int>>;
+      using JWV     = std::ranges::join_with_view<V, Pattern>;
+      using Iter    = std::ranges::iterator_t<JWV>;
+      using CIter   = std::ranges::iterator_t<const JWV>;
+      static_assert(!CanPreDecrement<Iter>);
+      static_assert(!CanPostDecrement<Iter>);
+      static_assert(!CanPreDecrement<CIter>);
+      static_assert(!CanPostDecrement<CIter>);
+    }
+
+    { // PatternBase does not model common range
+      using V       = std::ranges::owning_view<std::vector<std::vector<int>>>;
+      using Pattern = BasicVectorView<int, ViewProperties{.common = false}, bidirectional_iterator>;
+      using JWV     = std::ranges::join_with_view<V, Pattern>;
+      using Iter    = std::ranges::iterator_t<JWV>;
+      using CIter   = std::ranges::iterator_t<const JWV>;
+      static_assert(!CanPreDecrement<Iter>);
+      static_assert(!CanPostDecrement<Iter>);
+      static_assert(!CanPreDecrement<CIter>);
+      static_assert(!CanPostDecrement<CIter>);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/deref.pass.cpp
new file mode 100644
index 00000000000000..2491e8bf75f701
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/deref.pass.cpp
@@ -0,0 +1,144 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <ranges>
+
+// constexpr decltype(auto) operator*() const;
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <string>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+#include "../types.h"
+
+constexpr bool test() {
+  { // Result of `operator*` is (maybe const) lvalue reference
+    using V       = std::ranges::owning_view<std::vector<std::string>>;
+    using Pattern = std::ranges::owning_view<std::string>;
+    using JWV     = std::ranges::join_with_view<V, Pattern>;
+
+    JWV jwv(V{{"ab", "cd", "ef"}}, Pattern{"><"});
+
+    {
+      auto it                                  = jwv.begin();
+      std::same_as<char&> decltype(auto) v_ref = *std::as_const(it);
+      assert(v_ref == 'a');
+      std::ranges::advance(it, 2);
+      std::same_as<char&> decltype(auto) pattern_ref = *it;
+      assert(pattern_ref == '>');
+    }
+
+    {
+      auto cit                                        = std::as_const(jwv).begin();
+      std::same_as<const char&> decltype(auto) cv_ref = *cit;
+      assert(cv_ref == 'a');
+      std::ranges::advance(cit, 3);
+      std::same_as<const char&> decltype(auto) cpattern_ref = *std::as_const(cit);
+      assert(cpattern_ref == '<');
+    }
+  }
+
+  { // Result of `operator*` is const lvalue reference
+    using V       = std::ranges::owning_view<std::vector<std::string_view>>;
+    using Pattern = std::string_view;
+    using JWV     = std::ranges::join_with_view<V, Pattern>;
+
+    JWV jwv(V{{"123", "456", "789"}}, Pattern{"._."});
+
+    {
+      auto it                                        = jwv.begin();
+      std::same_as<const char&> decltype(auto) v_ref = *it;
+      assert(v_ref == '1');
+      std::ranges::advance(it, 3);
+      std::same_as<const char&> decltype(auto) pattern_ref = *std::as_const(it);
+      assert(pattern_ref == '.');
+    }
+
+    {
+      auto cit                                        = std::as_const(jwv).begin();
+      std::same_as<const char&> decltype(auto) cv_ref = *std::as_const(cit);
+      assert(cv_ref == '1');
+      std::ranges::advance(cit, 4);
+      std::same_as<const char&> decltype(auto) cpattern_ref = *cit;
+      assert(cpattern_ref == '_');
+    }
+  }
+
+  { // Result of `operator*` is prvalue
+    using V       = std::vector<std::string_view>;
+    using Pattern = RvalueVector<char>;
+    using JWV     = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;
+
+    JWV jwv(V{"x^2", "y^2", "z^2"}, Pattern{{' ', '+', ' '}});
+
+    {
+      auto it                                 = jwv.begin();
+      std::same_as<char> decltype(auto) v_ref = *std::as_const(it);
+      assert(v_ref == 'x');
+      std::ranges::advance(it, 3);
+      std::same_as<char> decltype(auto) pattern_ref = *it;
+      assert(pattern_ref == ' ');
+    }
+
+    {
+      auto cit                                 = std::as_const(jwv).begin();
+      std::same_as<char> decltype(auto) cv_ref = *cit;
+      assert(cv_ref == 'x');
+      std::ranges::advance(cit, 4);
+      std::same_as<char> decltype(auto) cpattern_ref = *std::as_const(cit);
+      assert(cpattern_ref == '+');
+    }
+  }
+
+  { // Result of `operator*` is (maybe const) rvalue reference
+    using Inner   = std::ranges::as_rvalue_view<std::ranges::owning_view<std::string>>;
+    using V       = std::ranges::owning_view<std::vector<Inner>>;
+    using Pattern = std::ranges::as_rvalue_view<std::ranges::owning_view<std::array<char, 2>>>;
+    using JWV     = std::ranges::join_with_view<V, Pattern>;
+
+    std::vector<Inner> vec;
+    vec.emplace_back(Inner{{"x*y"}});
+    vec.emplace_back(Inner{{"y*z"}});
+    vec.emplace_back(Inner{{"z*x"}});
+    JWV jwv(V(std::move(vec)), Pattern(std::array{',', ' '}));
+
+    {
+      auto it                                   = jwv.begin();
+      std::same_as<char&&> decltype(auto) v_ref = *it;
+      assert(v_ref == 'x');
+      std::ranges::advance(it, 3);
+      std::same_as<char&&> decltype(auto) pattern_ref = *std::as_const(it);
+      assert(pattern_ref == ',');
+    }
+
+    {
+      auto cit                                         = std::as_const(jwv).begin();
+      std::same_as<const char&&> decltype(auto) cv_ref = *std::as_const(cit);
+      assert(cv_ref == 'x');
+      std::ranges::advance(cit, 4);
+      std::same_as<const char&&> decltype(auto) cpattern_ref = *cit;
+      assert(cpattern_ref == ' ');
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/eq.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/eq.pass.cpp
new file mode 100644
index 00000000000000..f02e0c9169fd16
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/eq.pass.cpp
@@ -0,0 +1,256 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <ranges>
+
+// friend constexpr bool operator==(const iterator& x, const iterator& y)
+//   requires ref-is-glvalue && forward_range<Base> &&
+//            equality_comparable<InnerIter>;
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <utility>
+
+#include "../types.h"
+#include "test_comparisons.h"
+
+template <class I1, class I2 = I1>
+concept CanEq = requires(const I1& i1, const I2& i2) {
+  { i1 == i2 } -> std::same_as<bool>;
+  { i2 == i1 } -> std::same_as<bool>;
+  { i1 != i2 } -> std::same_as<bool>;
+  { i2 != i1 } -> std::same_as<bool>;
+};
+
+constexpr bool test() {
+  { // `V` and `Pattern` are not empty. Test return types too.
+    using V       = std::array<std::array<int, 2>, 3>;
+    using Pattern = std::array<long, 1>;
+    using JWV     = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;
+
+    using Iter  = std::ranges::iterator_t<JWV>;
+    using CIter = std::ranges::iterator_t<const JWV>;
+    static_assert(CanEq<Iter>);
+    static_assert(CanEq<CIter>);
+    static_assert(CanEq<Iter, CIter>);
+
+    JWV jwv(V{{{9, 8}, {7, 6}, {5, 4}}}, Pattern{0L});
+
+    Iter it1 = jwv.begin();
+    assert(*it1 == 9);
+    assert(testEquality(it1, it1, true));
+
+    Iter it2 = std::ranges::prev(jwv.end());
+    assert(*it2 == 4);
+    assert(testEquality(it2, it2, true));
+    assert(testEquality(it1, it2, false));
+
+    CIter cit1 = std::as_const(jwv).begin();
+    assert(*cit1 == 9);
+    assert(testEquality(cit1, cit1, true));
+    assert(testEquality(it1, cit1, true));
+    assert(testEquality(it2, cit1, false));
+
+    CIter cit2 = std::ranges::prev(std::as_const(jwv).end());
+    assert(*cit2 == 4);
+    assert(testEquality(cit2, cit2, true));
+    assert(testEquality(cit1, cit2, false));
+    assert(testEquality(it1, cit2, false));
+    assert(testEquality(it2, cit2, true));
+
+    // `it1.inner_it_` and `it2.inner_it_` are equal, but `it1.outer_it_` and `it2.outer_it_` are not.
+    std::ranges::advance(it1, 2);
+    assert(*it1 == 0);
+    std::ranges::advance(it2, -2);
+    assert(*it2 == 0);
+    assert(testEquality(it1, it2, false));
+
+    // `cit1.inner_it_` and `cit2.inner_it_` are equal, but `cit1.outer_it_` and `cit2.outer_it_` are not.
+    std::ranges::advance(cit1, 2);
+    assert(*cit1 == 0);
+    assert(testEquality(it1, cit1, true));
+    std::ranges::advance(cit2, -2);
+    assert(*cit2 == 0);
+    assert(testEquality(it2, cit2, true));
+    assert(testEquality(cit1, cit2, false));
+
+    // `it1.inner_it_` and `it2.inner_it_` are equal, `it1.outer_it_` and `it2.outer_it_` are equal too.
+    // `it1.inner_it_index()` and `it2.inner_it_index()` are equal to 1.
+    ++it1;
+    assert(*it1 == 7);
+    std::ranges::advance(it2, -2);
+    assert(*it2 == 7);
+    assert(testEquality(it1, it2, true));
+
+    // `cit1.inner_it_` and `cit2.inner_it_` are equal, `cit1.outer_it_` and `cit2.outer_it_` are equal too.
+    // `cit1.inner_it_index()` and `cit2.inner_it_index()` are equal to 1.
+    ++cit1;
+    assert(*cit1 == 7);
+    assert(testEquality(it1, cit1, true));
+    std::ranges::advance(cit2, -2);
+    assert(*cit2 == 7);
+    assert(testEquality(it2, cit2, true));
+    assert(testEquality(cit1, cit2, true));
+
+    // `it1.inner_it_` and `it2.inner_it_` are equal, `it1.outer_it_` and `it2.outer_it_` are equal too.
+    // `it1.inner_it_index()` and `it2.inner_it_index()` are equal to 0.
+    --it1;
+    assert(*it1 == 0);
+    --it2;
+    assert(*it2 == 0);
+    assert(testEquality(it1, it2, true));
+
+    // `cit1.inner_it_` and `cit2.inner_it_` are equal, `cit1.outer_it_` and `cit2.outer_it_` are equal too.
+    // `cit1.inner_it_index()` and `cit2.inner_it_index()` are equal to 0.
+    --cit1;
+    assert(*cit1 == 0);
+    assert(testEquality(it1, cit1, true));
+    --cit2;
+    assert(*cit2 == 0);
+    assert(testEquality(it2, cit2, true));
+    assert(testEquality(cit2, cit2, true));
+  }
+
+  { // `InnerIter` models input iterator and equality comparable. `Pattern` is empty.
+    using Inner   = BasicVectorView<int, ViewProperties{.common = false}, EqComparableInputIter>;
+    using V       = std::vector<Inner>;
+    using Pattern = std::ranges::empty_view<int>;
+    using JWV     = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;
+
+    using Iter  = std::ranges::iterator_t<JWV>;
+    using CIter = std::ranges::iterator_t<const JWV>;
+    static_assert(CanEq<Iter>);
+    static_assert(CanEq<CIter>);
+    static_assert(!CanEq<CIter, Iter>);
+
+    JWV jwv(V{Inner{1, 2}, Inner{5, 6}, Inner{9, 0}}, Pattern{});
+
+    {
+      Iter it1 = jwv.begin();
+      assert(*it1 == 1);
+      Iter it2 = std::ranges::next(jwv.begin(), 2);
+      assert(*it2 == 5);
+      assert(testEquality(it1, it2, false));
+      ++it1;
+      ++it1;
+      assert(testEquality(it1, it2, true));
+      ++it1;
+      assert(testEquality(it1, it2, false));
+    }
+
+    {
+      CIter cit1 = std::as_const(jwv).begin();
+      assert(*cit1 == 1);
+      CIter cit2 = std::ranges::next(std::as_const(jwv).begin(), 2);
+      assert(*cit2 == 5);
+      assert(testEquality(cit1, cit2, false));
+      ++cit1;
+      ++cit1;
+      assert(testEquality(cit1, cit2, true));
+      ++cit1;
+      assert(testEquality(cit1, cit2, false));
+    }
+  }
+
+  { // `Pattern` is not empty. Some elements of `V` are.
+    using Inner   = BasicVectorView<int, ViewProperties{.common = false}, EqComparableInputIter>;
+    using V       = BasicVectorView<Inner, ViewProperties{}, forward_iterator>;
+    using Pattern = BasicVectorView<int, ViewProperties{.common = false}, forward_iterator>;
+    using JWV     = std::ranges::join_with_view<V, Pattern>;
+
+    using Iter  = std::ranges::iterator_t<JWV>;
+    using CIter = std::ranges::iterator_t<const JWV>;
+    static_assert(CanEq<Iter>);
+    static_assert(CanEq<CIter>);
+    static_assert(!CanEq<CIter, Iter>);
+
+    JWV jwv(V{Inner{1}, Inner{}, Inner{27}}, Pattern{0});
+
+    {
+      Iter it1 = jwv.begin();
+      assert(*it1 == 1);
+      ++it1;
+      assert(*it1 == 0);
+      Iter it2 = jwv.begin();
+      assert(testEquality(it1, it2, false));
+      ++it2;
+      assert(testEquality(it1, it2, true));
+
+      ++it2;
+      assert(*it1 == *it2);
+      assert(testEquality(it1, it2, false));
+
+      std::ranges::advance(it1, 2);
+      ++it2;
+      assert(*it1 == *it2);
+      assert(testEquality(it1, it2, true));
+    }
+
+    {
+      CIter cit1 = std::as_const(jwv).begin();
+      assert(*cit1 == 1);
+      ++cit1;
+      assert(*cit1 == 0);
+      CIter cit2 = std::as_const(jwv).begin();
+      assert(testEquality(cit1, cit2, false));
+      ++cit2;
+      assert(testEquality(cit1, cit2, true));
+
+      ++cit2;
+      assert(*cit1 == *cit2);
+      assert(testEquality(cit1, cit2, false));
+
+      std::ranges::advance(cit1, 2);
+      ++cit2;
+      assert(*cit1 == *cit2);
+      assert(testEquality(cit1, cit2, true));
+    }
+  }
+
+  { // `ref-is-glvalue` is false
+    using Inner   = std::vector<int>;
+    using V       = RvalueVector<Inner>;
+    using Pattern = std::ranges::empty_view<int>;
+    using JWV     = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;
+    using Iter    = std::ranges::iterator_t<JWV>;
+    static_assert(!CanEq<Iter>);
+  }
+
+  { // `Base` does not model forward range
+    using Inner   = std::vector<int>;
+    using V       = BasicVectorView<Inner, ViewProperties{}, DefaultCtorInputIter>;
+    using Pattern = std::ranges::empty_view<int>;
+    using JWV     = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;
+    using Iter    = std::ranges::iterator_t<JWV>;
+    static_assert(!CanEq<Iter>);
+  }
+
+  { // `InnerIter` does not model equality comparable
+    using Inner   = BasicVectorView<int, ViewProperties{.common = false}, cpp20_input_iterator>;
+    using V       = std::vector<Inner>;
+    using Pattern = std::ranges::empty_view<int>;
+    using JWV     = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;
+    using Iter    = std::ranges::iterator_t<JWV>;
+    using CIter   = std::ranges::iterator_t<const JWV>;
+    static_assert(!CanEq<Iter>);
+    static_assert(!CanEq<CIter>);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/increment.pass.cpp
new file mode 100644
index 00000000000000..c100d73aff34b7
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/increment.pass.cpp
@@ -0,0 +1,320 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <ranges>
+
+// constexpr iterator& operator++();
+// constexpr void operator++(int);
+// constexpr iterator operator++(int)
+//   requires ref-is-glvalue && forward_iterator<OuterIter> &&
+//            forward_iterator<InnerIter>;
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <type_traits>
+#include <vector>
+
+#include "../types.h"
+
+template <class I>
+concept CanPreIncrement = requires(I& i) { ++i; };
+
+template <class I>
+concept CanPostIncrement = requires(I& i) { i++; };
+
+constexpr void test_pre_increment() {
+  { // `V` and `Pattern` are not empty. Test return type too.
+    using V       = std::array<std::array<int, 2>, 3>;
+    using Pattern = std::array<int, 2>;
+    using JWV     = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;
+
+    using Iter  = std::ranges::iterator_t<JWV>;
+    using CIter = std::ranges::iterator_t<const JWV>;
+    static_assert(CanPreIncrement<Iter>);
+    static_assert(!CanPreIncrement<const Iter>);
+    static_assert(CanPreIncrement<CIter>);
+    static_assert(!CanPreIncrement<const CIter>);
+
+    JWV jwv(V{{{1, 1}, {2, 2}, {3, 3}}}, Pattern{0, 0});
+
+    {
+      auto it = jwv.begin();
+      assert(*it == 1);
+      std::same_as<Iter&> decltype(auto) it_ref = ++it;
+      assert(it_ref == it);
+      ++it;
+      assert(*it == 0);
+      ++it_ref;
+      ++it_ref;
+      assert(*it_ref == 2);
+      ++it;
+      ++it_ref;
+      assert(*it == 0);
+    }
+
+    {
+      auto cit = std::as_const(jwv).begin();
+      assert(*cit == 1);
+      std::same_as<CIter&> decltype(auto) cit_ref = ++cit;
+      assert(cit_ref == cit);
+      ++cit;
+      assert(*cit == 0);
+      ++cit_ref;
+      ++cit_ref;
+      assert(*cit_ref == 2);
+      ++cit;
+      ++cit_ref;
+      assert(*cit == 0);
+    }
+  }
+
+#if !defined(TEST_COMPILER_GCC) // GCC c++/101777
+
+  { // `Pattern` is empty, `V` is not.
+    using V       = std::array<std::vector<int>, 3>;
+    using Pattern = std::vector<int>;
+    using JWV     = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;
+
+    JWV jwv(V{{{-1}, {-2}, {-3}}}, Pattern{});
+
+    {
+      auto it = jwv.begin();
+      assert(*it == -1);
+      ++it;
+      assert(*it == -2);
+      ++it;
+      assert(*it == -3);
+      ++it;
+      assert(it == jwv.end());
+    }
+
+    {
+      auto cit = std::as_const(jwv).begin();
+      assert(*cit == -1);
+      ++cit;
+      assert(*cit == -2);
+      ++cit;
+      assert(*cit == -3);
+      ++cit;
+      assert(cit == std::as_const(jwv).end());
+    }
+  }
+
+  { // `V` has empty subrange in the middle, `Pattern` is not empty.
+    using V       = std::array<std::vector<int>, 3>;
+    using Pattern = std::ranges::single_view<int>;
+    using JWV     = std::ranges::join_with_view<std::ranges::owning_view<V>, Pattern>;
+
+    JWV jwv(V{{{1}, {}, {3}}}, Pattern{0});
+
+    {
+      auto it = jwv.begin();
+      assert(*it == 1);
+      ++it;
+      assert(*it == 0);
+      ++it;
+      assert(*it == 0);
+      ++it;
+      assert(*it == 3);
+    }
+
+    {
+      auto cit = std::as_const(jwv).begin();
+      assert(*cit == 1);
+      ++cit;
+      assert(*cit == 0);
+      ++cit;
+      assert(*cit == 0);
+      ++cit;
+      assert(*cit == 3);
+    }
+  }
+
+  { // Only first element of `V` is not empty. `Pattern` is empty.
+    using V       = std::array<std::vector<int>, 3>;
+    using Pattern = std::ranges::empty_view<int>;
+    using JWV     = std::ranges::join_with_view<std::ranges::owning_view<V>, Pattern>;
+
+    JWV jwv(V{{{777}, {}, {}}}, Pattern{});
+
+    {
+      auto it = jwv.begin();
+      assert(*it == 777);
+      ++it;
+      assert(it == jwv.end());
+    }
+
+    {
+      auto cit = std::as_const(jwv).begin();
+      assert(*cit == 777);
+      ++cit;
+      assert(cit == std::as_const(jwv).end());
+    }
+  }
+
+#endif // !defined(TEST_COMPILER_GCC)
+
+  { // Only last element of `V` is not empty. `Pattern` is empty. `V` models input range and `ref-is-glvalue` is false.
+    using Inner   = RvalueVector<std::string>;
+    using V       = BasicView<Inner, ViewProperties{}, DefaultCtorInputIter>;
+    using Pattern = std::ranges::empty_view<char>;
+    using JWV     = std::ranges::join_with_view<V, Pattern>;
+
+    JWV jwv(V{{}, {}, {'a'}}, Pattern{});
+
+    auto it = jwv.begin();
+    assert(*it == 'a');
+    ++it;
+    assert(it == jwv.end());
+  }
+
+  { // Only first element of `V` is not empty. `Pattern` is not empty.
+    // `V` models input range and `ref-is-glvalue` is false.
+    using Inner   = RvalueVector<std::string>;
+    using V       = BasicView<Inner, ViewProperties{}, DefaultCtorInputIter>;
+    using Pattern = std::ranges::single_view<char>;
+    using JWV     = std::ranges::join_with_view<V, Pattern>;
+
+    JWV jwv(V{{'b'}, {}, {}}, Pattern{'.'});
+
+    auto it = jwv.begin();
+    assert(*it == 'b');
+    ++it;
+    assert(*it == '.');
+    ++it;
+    assert(*it == '.');
+    ++it;
+    assert(it == jwv.end());
+  }
+}
+
+constexpr void test_post_increment() {
+  { // `V` and `Pattern` are not empty. Test return type too.
+    using V       = std::array<std::array<int, 3>, 2>;
+    using Pattern = std::array<int, 1>;
+    using JWV     = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;
+
+    using Iter  = std::ranges::iterator_t<JWV>;
+    using CIter = std::ranges::iterator_t<const JWV>;
+    static_assert(CanPostIncrement<Iter>);
+    static_assert(!CanPostIncrement<const Iter>);
+    static_assert(CanPostIncrement<CIter>);
+    static_assert(!CanPostIncrement<const CIter>);
+
+    JWV jwv(V{{{6, 5, 4}, {3, 2, 1}}}, Pattern{-5});
+
+    {
+      auto it = jwv.begin();
+      assert(*it == 6);
+      std::same_as<Iter> decltype(auto) it_copy = it++;
+      assert(++it_copy == it);
+      it++;
+      it++;
+      assert(*it == -5);
+      it_copy++;
+      it_copy++;
+      assert(*it_copy == -5);
+      it++;
+      it_copy++;
+      assert(*it == 3);
+      assert(*it_copy == 3);
+    }
+
+    {
+      auto cit = std::as_const(jwv).begin();
+      assert(*cit == 6);
+      std::same_as<CIter> decltype(auto) cit_copy = cit++;
+      assert(++cit_copy == cit);
+      cit++;
+      cit++;
+      assert(*cit == -5);
+      cit_copy++;
+      cit_copy++;
+      assert(*cit_copy == -5);
+      cit++;
+      cit_copy++;
+      assert(*cit == 3);
+      assert(*cit_copy == 3);
+    }
+  }
+
+  { // `Pattern` is empty, `V` is not. `ref-is-glvalue` is false.
+    using Inner   = std::vector<int>;
+    using V       = RvalueVector<Inner>;
+    using Pattern = std::ranges::empty_view<int>;
+    using JWV     = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;
+
+    JWV jwv(V{Inner{-3}, Inner{-2}, Inner{-1}}, Pattern{});
+
+    auto it = jwv.begin();
+    assert(*it == -3);
+    it++;
+    assert(*it == -2);
+    it++;
+    assert(*it == -1);
+    it++;
+    assert(it == jwv.end());
+    static_assert(std::is_void_v<decltype(it++)>);
+  }
+
+  { // `V` has empty subrange in the middle, `Pattern` is not empty. OuterIter does not model forward iterator.
+    using Inner   = std::vector<int>;
+    using V       = BasicVectorView<Inner, ViewProperties{.common = false}, cpp20_input_iterator>;
+    using Pattern = std::ranges::single_view<int>;
+    using JWV     = std::ranges::join_with_view<V, Pattern>;
+
+    JWV jwv(V{Inner{7}, {}, Inner{9}}, Pattern{8});
+
+    auto it = jwv.begin();
+    assert(*it == 7);
+    it++;
+    assert(*it == 8);
+    it++;
+    assert(*it == 8);
+    it++;
+    assert(*it == 9);
+    it++;
+    assert(it == jwv.end());
+    static_assert(std::is_void_v<decltype(it++)>);
+  }
+
+#if !defined(TEST_COMPILER_GCC) // GCC c++/101777
+  { // Only first element of `V` is not empty. `Pattern` is empty. InnerIter does not model forward iterator.
+    using Inner   = BasicVectorView<char32_t, ViewProperties{.common = false}, cpp17_input_iterator>;
+    using V       = std::array<Inner, 3>;
+    using Pattern = std::ranges::empty_view<char32_t>;
+    using JWV     = std::ranges::join_with_view<std::ranges::owning_view<V>, Pattern>;
+
+    JWV jwv(V{Inner{U'?'}, Inner{}, Inner{}}, Pattern{});
+
+    auto it = jwv.begin();
+    assert(*it == U'?');
+    it++;
+    assert(it == jwv.end());
+    static_assert(std::is_void_v<decltype(it++)>);
+  }
+#endif // !defined(TEST_COMPILER_GCC)
+}
+
+constexpr bool test() {
+  test_pre_increment();
+  test_post_increment();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_move.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_move.pass.cpp
new file mode 100644
index 00000000000000..147626f1a7ec34
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_move.pass.cpp
@@ -0,0 +1,340 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <ranges>
+
+// friend constexpr decltype(auto) iter_move(const iterator& x) {
+//   using rvalue_reference = common_reference_t<
+//     iter_rvalue_reference_t<InnerIter>,
+//     iter_rvalue_reference_t<PatternIter>>;
+//   return visit<rvalue_reference>(ranges::iter_move, x.inner_it_);
+// }
+
+#include <ranges>
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include "../types.h"
+
+class MoveOnlyInt {
+public:
+  enum Status { constructed, move_constructed, moved_from_this };
+
+  MoveOnlyInt() = default;
+  constexpr MoveOnlyInt(int val) : val_(val) {}
+
+  constexpr MoveOnlyInt(MoveOnlyInt&& other) noexcept : val_(other.val_), status_(move_constructed) {
+    other.val_    = -1;
+    other.status_ = moved_from_this;
+  }
+
+  constexpr MoveOnlyInt(const MoveOnlyInt&& other) noexcept : val_(other.val_), status_(move_constructed) {
+    other.val_    = -1;
+    other.status_ = moved_from_this;
+  }
+
+  MoveOnlyInt(const MoveOnlyInt&) { assert(false); } // Should never be called in this test.
+
+  MoveOnlyInt& operator=(MoveOnlyInt&&) { // Should never be called in this test.
+    assert(false);
+    return *this;
+  }
+
+  constexpr Status get_status() const { return status_; }
+
+  friend constexpr bool operator==(const MoveOnlyInt& left, int right) { return left.val_ == right; }
+  friend constexpr bool operator==(const MoveOnlyInt& left, const MoveOnlyInt& right) {
+    return left.val_ == right.val_;
+  }
+
+private:
+  mutable int val_       = -1;
+  mutable Status status_ = constructed;
+};
+
+static_assert(std::movable<MoveOnlyInt>);
+
+template <class T>
+class ProxyRvalue {
+  T val_;
+
+public:
+  constexpr ProxyRvalue(T val) : val_(std::move(val)) {}
+
+  ProxyRvalue(ProxyRvalue&&)            = default;
+  ProxyRvalue& operator=(ProxyRvalue&&) = default;
+
+  constexpr explicit operator T&&() noexcept { return std::move(val_); }
+};
+
+static_assert(std::common_reference_with<ProxyRvalue<int>, int>);
+static_assert(std::common_reference_with<ProxyRvalue<MoveOnlyInt>, MoveOnlyInt>);
+
+template <std::bidirectional_iterator It>
+class ProxyOnIterMoveIter {
+  It it_ = It();
+
+public:
+  using value_type      = std::iter_value_t<It>;
+  using difference_type = std::iter_difference_t<It>;
+
+  ProxyOnIterMoveIter() = default;
+  constexpr ProxyOnIterMoveIter(It it) : it_(std::move(it)) {}
+
+  constexpr decltype(auto) operator*() const { return *it_; }
+
+  constexpr ProxyOnIterMoveIter& operator++() {
+    ++it_;
+    return *this;
+  }
+
+  constexpr ProxyOnIterMoveIter operator++(int) {
+    ProxyOnIterMoveIter copy = *this;
+    ++it_;
+    return copy;
+  }
+
+  constexpr ProxyOnIterMoveIter& operator--() {
+    --it_;
+    return *this;
+  }
+
+  constexpr ProxyOnIterMoveIter operator--(int) {
+    ProxyOnIterMoveIter copy = *this;
+    --it_;
+    return copy;
+  }
+
+  friend bool operator==(const ProxyOnIterMoveIter&, const ProxyOnIterMoveIter&) = default;
+
+  friend constexpr ProxyRvalue<value_type> iter_move(const ProxyOnIterMoveIter iter) {
+    return ProxyRvalue<value_type>{std::ranges::iter_move(iter.it_)};
+  }
+};
+
+template <class It>
+ProxyOnIterMoveIter(It) -> ProxyOnIterMoveIter<It>;
+
+static_assert(std::bidirectional_iterator<ProxyOnIterMoveIter<int*>>);
+
+constexpr bool test() {
+  { // Test `iter_move` when result is true rvalue reference. Test return types.
+    using V       = std::array<std::array<char, 1>, 2>;
+    using Pattern = std::array<char, 1>;
+    using JWV     = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;
+
+    JWV jwv(V{{{'0'}, {'1'}}}, Pattern{','});
+
+    {
+      auto it                                     = jwv.begin();
+      std::same_as<char&&> decltype(auto) v_rref1 = iter_move(it);
+      std::same_as<char&&> decltype(auto) v_rref2 = iter_move(std::as_const(it));
+      std::same_as<char&&> decltype(auto) v_rref3 = std::ranges::iter_move(it);
+      std::same_as<char&&> decltype(auto) v_rref4 = std::ranges::iter_move(std::as_const(it));
+      assert(std::ranges::equal(std::array{v_rref1, v_rref2, v_rref3, v_rref4}, std::views::repeat('0', 4)));
+
+      ++it; // `it` points to element of `Pattern` from here
+      std::same_as<char&&> decltype(auto) pattern_rref1 = iter_move(it);
+      std::same_as<char&&> decltype(auto) pattern_rref2 = iter_move(std::as_const(it));
+      std::same_as<char&&> decltype(auto) pattern_rref3 = std::ranges::iter_move(it);
+      std::same_as<char&&> decltype(auto) pattern_rref4 = std::ranges::iter_move(std::as_const(it));
+      assert(std::ranges::equal(
+          std::array{pattern_rref1, pattern_rref2, pattern_rref3, pattern_rref4}, std::views::repeat(',', 4)));
+    }
+
+    {
+      auto cit                                           = std::prev(std::as_const(jwv).end());
+      std::same_as<const char&&> decltype(auto) cv_rref1 = iter_move(cit);
+      std::same_as<const char&&> decltype(auto) cv_rref2 = iter_move(std::as_const(cit));
+      std::same_as<const char&&> decltype(auto) cv_rref3 = std::ranges::iter_move(cit);
+      std::same_as<const char&&> decltype(auto) cv_rref4 = std::ranges::iter_move(std::as_const(cit));
+      assert(std::ranges::equal(std::array{cv_rref1, cv_rref2, cv_rref3, cv_rref4}, std::views::repeat('1', 4)));
+
+      cit--; // `cit` points to element of `Pattern` from here
+      std::same_as<const char&&> decltype(auto) cpattern_rref1 = iter_move(cit);
+      std::same_as<const char&&> decltype(auto) cpattern_rref2 = iter_move(std::as_const(cit));
+      std::same_as<const char&&> decltype(auto) cpattern_rref3 = std::ranges::iter_move(cit);
+      std::same_as<const char&&> decltype(auto) cpattern_rref4 = std::ranges::iter_move(std::as_const(cit));
+      assert(std::ranges::equal(
+          std::array{cpattern_rref1, cpattern_rref2, cpattern_rref3, cpattern_rref4}, std::views::repeat(',', 4)));
+    }
+  }
+
+  { // Test `iter_move` when result is true rvalue reference. Test moving.
+    using Inner   = std::vector<MoveOnlyInt>;
+    using V       = std::vector<Inner>;
+    using Pattern = std::vector<MoveOnlyInt>;
+    using JWV     = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;
+
+    V v;
+    v.reserve(2);
+    v.emplace_back(std::ranges::to<Inner>(std::views::iota(0, 4)));
+    v.emplace_back(std::ranges::to<Inner>(std::views::iota(12, 16)));
+    JWV jwv(std::move(v), std::ranges::to<Pattern>(std::views::iota(4, 12)));
+    assert(std::ranges::all_of(jwv, [](const MoveOnlyInt& i) { return i.get_status() == MoveOnlyInt::constructed; }));
+
+    {
+      using enum MoveOnlyInt::Status;
+      std::vector<MoveOnlyInt> values;
+      values.reserve(8);
+
+      auto it = jwv.begin();
+      values.emplace_back(iter_move(it));
+      ++it;
+      values.emplace_back(iter_move(std::as_const(it)));
+      it++;
+      values.emplace_back(std::ranges::iter_move(it));
+      ++it;
+      values.emplace_back(std::ranges::iter_move(std::as_const(it)));
+      it++; // `it` points to element of `Pattern` from here
+      values.emplace_back(iter_move(it));
+      ++it;
+      values.emplace_back(iter_move(std::as_const(it)));
+      it++;
+      values.emplace_back(std::ranges::iter_move(it));
+      ++it;
+      values.emplace_back(std::ranges::iter_move(std::as_const(it)));
+
+      assert(std::ranges::equal(values, std::views::iota(0, 8)));
+      assert(std::ranges::all_of(values, [](const MoveOnlyInt& i) { return i.get_status() == move_constructed; }));
+    }
+
+    {
+      using enum MoveOnlyInt::Status;
+      std::vector<MoveOnlyInt> values;
+      values.reserve(8);
+
+      auto cit = std::prev(std::as_const(jwv).end());
+      values.emplace_back(iter_move(cit));
+      cit--;
+      values.emplace_back(iter_move(std::as_const(cit)));
+      --cit;
+      values.emplace_back(std::ranges::iter_move(cit));
+      cit--;
+      values.emplace_back(std::ranges::iter_move(std::as_const(cit)));
+      --cit; // `it` points to element of `Pattern` from here
+      values.emplace_back(iter_move(cit));
+      cit--;
+      values.emplace_back(iter_move(std::as_const(cit)));
+      --cit;
+      values.emplace_back(std::ranges::iter_move(cit));
+      cit--;
+      values.emplace_back(std::ranges::iter_move(std::as_const(cit)));
+
+      assert(std::ranges::equal(std::views::reverse(values), std::views::iota(8, 16)));
+      assert(std::ranges::all_of(values, [](const MoveOnlyInt& i) { return i.get_status() == move_constructed; }));
+    }
+
+    assert(
+        std::ranges::all_of(jwv, [](const MoveOnlyInt& i) { return i.get_status() == MoveOnlyInt::moved_from_this; }));
+  }
+
+  { // Test `iter_move` when result is proxy rvalue reference. Test return types and moving.
+    using Inner   = std::vector<MoveOnlyInt>;
+    using V       = std::vector<Inner>;
+    using Pattern = BasicVectorView<MoveOnlyInt, ViewProperties{}, ProxyOnIterMoveIter>;
+    using JWV     = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;
+
+    using RRef = ProxyRvalue<MoveOnlyInt>;
+    static_assert(std::same_as<RRef, std::ranges::range_rvalue_reference_t<JWV>>);
+
+    V v;
+    v.reserve(2);
+    v.emplace_back(std::ranges::to<Inner>(std::views::iota(0, 4)));
+    v.emplace_back(std::ranges::to<Inner>(std::views::iota(12, 16)));
+    JWV jwv(std::move(v), Pattern{std::ranges::to<std::vector<MoveOnlyInt>>(std::views::iota(4, 12))});
+    assert(std::ranges::all_of(jwv, [](const MoveOnlyInt& i) { return i.get_status() == MoveOnlyInt::constructed; }));
+
+    {
+      using enum MoveOnlyInt::Status;
+      std::vector<MoveOnlyInt> values;
+      values.reserve(8);
+
+      auto it                                 = jwv.begin();
+      std::same_as<RRef> decltype(auto) rref1 = iter_move(it);
+      values.emplace_back(std::move(rref1));
+      ++it;
+      std::same_as<RRef> decltype(auto) rref2 = iter_move(std::as_const(it));
+      values.emplace_back(rref2);
+      it++;
+      std::same_as<RRef> decltype(auto) rref3 = std::ranges::iter_move(it);
+      values.emplace_back(rref3);
+      ++it;
+      std::same_as<RRef> decltype(auto) rref4 = std::ranges::iter_move(std::as_const(it));
+      values.emplace_back(rref4);
+      it++; // `it` points to element of `Pattern` from here
+      std::same_as<RRef> decltype(auto) rref5 = iter_move(it);
+      values.emplace_back(rref5);
+      ++it;
+      std::same_as<RRef> decltype(auto) rref6 = iter_move(std::as_const(it));
+      values.emplace_back(rref6);
+      it++;
+      std::same_as<RRef> decltype(auto) rref7 = std::ranges::iter_move(it);
+      values.emplace_back(rref7);
+      ++it;
+      std::same_as<RRef> decltype(auto) rref8 = std::ranges::iter_move(std::as_const(it));
+      values.emplace_back(rref8);
+
+      assert(std::ranges::equal(values, std::views::iota(0, 8)));
+      assert(std::ranges::all_of(values, [](const MoveOnlyInt& i) { return i.get_status() == move_constructed; }));
+    }
+
+    {
+      using enum MoveOnlyInt::Status;
+      std::vector<MoveOnlyInt> values;
+      values.reserve(8);
+
+      auto cit                                = std::prev(std::as_const(jwv).end());
+      std::same_as<RRef> decltype(auto) rref1 = iter_move(cit);
+      values.emplace_back(rref1);
+      cit--;
+      std::same_as<RRef> decltype(auto) rref2 = iter_move(std::as_const(cit));
+      values.emplace_back(rref2);
+      --cit;
+      std::same_as<RRef> decltype(auto) rref3 = std::ranges::iter_move(cit);
+      values.emplace_back(rref3);
+      cit--;
+      std::same_as<RRef> decltype(auto) rref4 = std::ranges::iter_move(std::as_const(cit));
+      values.emplace_back(rref4);
+      --cit; // `it` points to element of `Pattern` from here
+      std::same_as<RRef> decltype(auto) rref5 = iter_move(cit);
+      values.emplace_back(rref5);
+      cit--;
+      std::same_as<RRef> decltype(auto) rref6 = iter_move(std::as_const(cit));
+      values.emplace_back(rref6);
+      --cit;
+      std::same_as<RRef> decltype(auto) rref7 = std::ranges::iter_move(cit);
+      values.emplace_back(rref7);
+      cit--;
+      std::same_as<RRef> decltype(auto) rref8 = std::ranges::iter_move(std::as_const(cit));
+      values.emplace_back(rref8);
+
+      assert(std::ranges::equal(std::views::reverse(values), std::views::iota(8, 16)));
+      assert(std::ranges::all_of(values, [](const MoveOnlyInt& i) { return i.get_status() == move_constructed; }));
+    }
+
+    assert(
+        std::ranges::all_of(jwv, [](const MoveOnlyInt& i) { return i.get_status() == MoveOnlyInt::moved_from_this; }));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_swap.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_swap.pass.cpp
new file mode 100644
index 00000000000000..b4e66b92b38833
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_swap.pass.cpp
@@ -0,0 +1,81 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <ranges>
+
+// friend constexpr void iter_swap(const iterator& x, const iterator& y)
+//   requires indirectly_swappable<InnerIter, PatternIter> {
+//   visit(ranges::iter_swap, x.inner_it_, y.inner_it_);
+// }
+
+#include <ranges>
+
+#include <algorithm>
+#include <cassert>
+#include <span>
+#include <string>
+#include <string_view>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+template <class I>
+concept CanIterSwap = requires(I i) { iter_swap(i); };
+
+constexpr bool test() {
+  { // Test common usage
+    using V       = std::vector<std::string>;
+    using Pattern = std::string;
+    using JWV     = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;
+    using namespace std::string_view_literals;
+
+    JWV jwv(V{"std", "ranges", "views", "join_with_view"}, Pattern{":: "});
+    assert(std::ranges::equal(jwv, "std:: ranges:: views:: join_with_view"sv));
+
+    auto it = jwv.begin();
+    iter_swap(it, std::ranges::next(it, 2)); // Swap elements of the same inner range.
+    assert(std::ranges::equal(jwv, "dts:: ranges:: views:: join_with_view"sv));
+
+    std::ranges::advance(it, 3);
+    iter_swap(std::as_const(it), std::ranges::next(it, 2)); // Swap elements of the pattern.
+    assert(std::ranges::equal(jwv, "dts ::ranges ::views ::join_with_view"sv));
+
+    std::ranges::advance(it, 3);
+    const auto it2 = jwv.begin();
+    iter_swap(std::as_const(it), it2); // Swap elements of different inner ranges.
+    assert(std::ranges::equal(jwv, "rts ::danges ::views ::join_with_view"sv));
+
+    std::ranges::advance(it, 6);
+    iter_swap(std::as_const(it), it2); // Swap element from inner range with element from the pattern.
+    assert(std::ranges::equal(jwv, " tsr::dangesr::viewsr::join_with_view"sv));
+
+    static_assert(std::is_void_v<decltype(iter_swap(it, it))>);
+    static_assert(std::is_void_v<decltype(iter_swap(it2, it2))>);
+    static_assert(!CanIterSwap<std::ranges::iterator_t<const JWV>>);
+    static_assert(!CanIterSwap<const std::ranges::iterator_t<const JWV>>);
+  }
+
+  { // InnerIter and PatternIter don't model indirectly swappable
+    using JWV = std::ranges::join_with_view<std::span<std::string>, std::string_view>;
+    static_assert(!CanIterSwap<std::ranges::iterator_t<JWV>>);
+    static_assert(!CanIterSwap<const std::ranges::iterator_t<JWV>>);
+    static_assert(!CanIterSwap<std::ranges::iterator_t<const JWV>>);
+    static_assert(!CanIterSwap<const std::ranges::iterator_t<const JWV>>);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/types.compile.pass.cpp
new file mode 100644
index 00000000000000..ab34310211a958
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/types.compile.pass.cpp
@@ -0,0 +1,456 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <ranges>
+
+// using iterator_concept = see below;
+// using iterator_category = see below; // not always present
+// using value_type = see below;
+// using difference_type = see below;
+
+#include <ranges>
+
+#include <iterator>
+#include <vector>
+
+#include "../types.h"
+#include "test_iterators.h"
+
+namespace test_iterator_concept {
+template <template <class> class InnerIt>
+using InnerRange = BasicView<std::vector<int>, ViewProperties{}, InnerIt>;
+
+template <template <class> class It, template <class> class InnerIt>
+using View = BasicView<std::vector<InnerRange<InnerIt>>, ViewProperties{}, It>;
+
+template <template <class> class It, template <class> class InnerIt>
+using RvalueView = BasicView<RvalueVector<InnerRange<InnerIt>>, ViewProperties{}, It>;
+
+template <template <class> class It>
+using Pattern = BasicView<std::vector<int>, ViewProperties{}, It>;
+
+template <class V, class Pat>
+using IteratorConcept = std::ranges::iterator_t<std::ranges::join_with_view<V, Pat>>::iterator_concept;
+
+template <class V, class Pat, class Concept>
+concept IteratorConceptIs = std::same_as<IteratorConcept<V, Pat>, Concept>;
+
+// When `iterator<false>::iterator_concept` is `bidirectional_iterator_tag`
+static_assert(IteratorConceptIs<View<bidirectional_iterator, bidirectional_iterator>,
+                                Pattern<bidirectional_iterator>,
+                                std::bidirectional_iterator_tag>);
+
+// When `iterator<false>::iterator_concept` is `forward_iterator_tag`
+static_assert(IteratorConceptIs<View<forward_iterator, bidirectional_iterator>,
+                                Pattern<bidirectional_iterator>,
+                                std::forward_iterator_tag>);
+static_assert(IteratorConceptIs<View<bidirectional_iterator, forward_iterator>,
+                                Pattern<bidirectional_iterator>,
+                                std::forward_iterator_tag>);
+static_assert(IteratorConceptIs<View<bidirectional_iterator, bidirectional_iterator>,
+                                Pattern<forward_iterator>,
+                                std::forward_iterator_tag>);
+static_assert(IteratorConceptIs<View<forward_iterator, forward_iterator>,
+                                Pattern<bidirectional_iterator>,
+                                std::forward_iterator_tag>);
+static_assert(IteratorConceptIs<View<forward_iterator, bidirectional_iterator>,
+                                Pattern<forward_iterator>,
+                                std::forward_iterator_tag>);
+static_assert(IteratorConceptIs<View<bidirectional_iterator, forward_iterator>,
+                                Pattern<forward_iterator>,
+                                std::forward_iterator_tag>);
+static_assert(IteratorConceptIs<View<forward_iterator, forward_iterator>, //
+                                Pattern<forward_iterator>,
+                                std::forward_iterator_tag>);
+
+// When `iterator<false>::iterator_concept` is `input_iterator_tag`
+static_assert(IteratorConceptIs<View<DefaultCtorInputIter, forward_iterator>,
+                                Pattern<forward_iterator>,
+                                std::input_iterator_tag>);
+static_assert(IteratorConceptIs<View<forward_iterator, DefaultCtorInputIter>,
+                                Pattern<forward_iterator>,
+                                std::input_iterator_tag>);
+static_assert(IteratorConceptIs<View<DefaultCtorInputIter, DefaultCtorInputIter>,
+                                Pattern<forward_iterator>,
+                                std::input_iterator_tag>);
+static_assert(IteratorConceptIs<RvalueView<bidirectional_iterator, bidirectional_iterator>,
+                                Pattern<bidirectional_iterator>,
+                                std::input_iterator_tag>);
+static_assert(IteratorConceptIs<RvalueView<forward_iterator, forward_iterator>,
+                                Pattern<forward_iterator>,
+                                std::input_iterator_tag>);
+
+template <class V, class Pat>
+using ConstIteratorConcept = std::ranges::iterator_t<const std::ranges::join_with_view<V, Pat>>::iterator_concept;
+
+template <class V, class Pat, class Concept>
+concept ConstIteratorConceptIs = std::same_as<ConstIteratorConcept<V, Pat>, Concept>;
+
+// When `iterator<true>::iterator_concept` is `bidirectional_iterator_tag`
+static_assert(ConstIteratorConceptIs<View<bidirectional_iterator, bidirectional_iterator>,
+                                     Pattern<bidirectional_iterator>,
+                                     std::bidirectional_iterator_tag>);
+
+// When `iterator<true>::iterator_concept` is `forward_iterator_tag`
+static_assert(ConstIteratorConceptIs<View<forward_iterator, bidirectional_iterator>,
+                                     Pattern<bidirectional_iterator>,
+                                     std::forward_iterator_tag>);
+static_assert(ConstIteratorConceptIs<View<bidirectional_iterator, forward_iterator>,
+                                     Pattern<bidirectional_iterator>,
+                                     std::forward_iterator_tag>);
+static_assert(ConstIteratorConceptIs<View<bidirectional_iterator, bidirectional_iterator>,
+                                     Pattern<forward_iterator>,
+                                     std::forward_iterator_tag>);
+static_assert(ConstIteratorConceptIs<View<forward_iterator, forward_iterator>,
+                                     Pattern<bidirectional_iterator>,
+                                     std::forward_iterator_tag>);
+static_assert(ConstIteratorConceptIs<View<forward_iterator, bidirectional_iterator>,
+                                     Pattern<forward_iterator>,
+                                     std::forward_iterator_tag>);
+static_assert(ConstIteratorConceptIs<View<bidirectional_iterator, forward_iterator>,
+                                     Pattern<forward_iterator>,
+                                     std::forward_iterator_tag>);
+static_assert(ConstIteratorConceptIs<View<forward_iterator, forward_iterator>,
+                                     Pattern<forward_iterator>,
+                                     std::forward_iterator_tag>);
+
+// `iterator<true>::iterator_concept` cannot be `input_iterator_tag`
+} // namespace test_iterator_concept
+
+namespace test_iterator_category {
+template <template <class> class InnerIt>
+using InnerRange = BasicView<std::vector<float>, ViewProperties{}, InnerIt>;
+
+template <bool Common, template <class> class InnerIt>
+using MaybeCommonInnerRange = BasicView<std::vector<float>, ViewProperties{.common = Common}, InnerIt>;
+
+template <template <class> class It, template <class> class InnerIt>
+using View = BasicView<std::vector<InnerRange<InnerIt>>, ViewProperties{}, It>;
+
+template <template <class> class It, template <class> class InnerIt>
+using RvalueView = BasicView<RvalueVector<InnerRange<InnerIt>>, ViewProperties{}, It>;
+
+template <bool Common, template <class> class It, bool CommonInner, template <class> class InnerIt>
+using MaybeCommonView =
+    BasicView<std::vector<MaybeCommonInnerRange<CommonInner, InnerIt>>, ViewProperties{.common = Common}, It>;
+
+template <template <class> class It>
+using Pattern = BasicView<std::vector<float>, ViewProperties{}, It>;
+
+template <template <class> class It>
+using RvaluePattern = BasicView<RvalueVector<float>, ViewProperties{}, It>;
+
+template <bool Common, template <class> class It>
+using MaybeCommonPattern = BasicView<std::vector<float>, ViewProperties{.common = Common}, It>;
+
+template <class V, class Pattern>
+using IteratorCategory = std::ranges::iterator_t<std::ranges::join_with_view<V, Pattern>>::iterator_category;
+
+template <class V, class Pattern>
+concept HasIteratorCategory = requires { typename IteratorCategory<V, Pattern>; };
+
+template <class V, class Pat, class Category>
+concept IteratorCategoryIs = std::same_as<IteratorCategory<V, Pat>, Category>;
+
+// When `iterator<false>::iterator_category` is not defined
+static_assert(!HasIteratorCategory<View<cpp20_input_iterator, forward_iterator>, Pattern<forward_iterator>>);
+static_assert(!HasIteratorCategory<View<forward_iterator, cpp20_input_iterator>, Pattern<forward_iterator>>);
+static_assert(!HasIteratorCategory<View<forward_iterator, forward_iterator>, Pattern<cpp20_input_iterator>>);
+static_assert(!HasIteratorCategory<RvalueView<forward_iterator, forward_iterator>, Pattern<forward_iterator>>);
+static_assert(HasIteratorCategory<View<forward_iterator, forward_iterator>, Pattern<forward_iterator>>);
+
+// When
+//   is_reference_v<common_reference_t<iter_reference_t<InnerIter>,
+//                                     iter_reference_t<PatternIter>>>
+// has different values for `iterator<false>`
+static_assert(IteratorCategoryIs<View<forward_iterator, forward_iterator>,
+                                 RvaluePattern<forward_iterator>,
+                                 std::input_iterator_tag>);
+
+// When `iterator<false>::iterator_category` is `bidirectional_iterator_tag`
+static_assert(IteratorCategoryIs<MaybeCommonView<true, bidirectional_iterator, true, bidirectional_iterator>,
+                                 MaybeCommonPattern<true, bidirectional_iterator>,
+                                 std::bidirectional_iterator_tag>);
+static_assert(IteratorCategoryIs<MaybeCommonView<false, bidirectional_iterator, true, bidirectional_iterator>,
+                                 MaybeCommonPattern<true, bidirectional_iterator>,
+                                 std::bidirectional_iterator_tag>);
+
+// When `iterator<false>::iterator_category` is `forward_iterator_tag`
+static_assert(IteratorCategoryIs<MaybeCommonView<true, forward_iterator, true, bidirectional_iterator>,
+                                 MaybeCommonPattern<true, bidirectional_iterator>,
+                                 std::forward_iterator_tag>);
+static_assert(IteratorCategoryIs<MaybeCommonView<true, bidirectional_iterator, true, forward_iterator>,
+                                 MaybeCommonPattern<true, bidirectional_iterator>,
+                                 std::forward_iterator_tag>);
+static_assert(IteratorCategoryIs<MaybeCommonView<true, bidirectional_iterator, true, bidirectional_iterator>,
+                                 MaybeCommonPattern<true, forward_iterator>,
+                                 std::forward_iterator_tag>);
+static_assert(IteratorCategoryIs<MaybeCommonView<true, bidirectional_iterator, false, bidirectional_iterator>,
+                                 MaybeCommonPattern<true, bidirectional_iterator>,
+                                 std::forward_iterator_tag>);
+static_assert(IteratorCategoryIs<MaybeCommonView<true, bidirectional_iterator, true, bidirectional_iterator>,
+                                 MaybeCommonPattern<false, bidirectional_iterator>,
+                                 std::forward_iterator_tag>);
+static_assert(IteratorCategoryIs<MaybeCommonView<false, forward_iterator, false, forward_iterator>,
+                                 MaybeCommonPattern<false, forward_iterator>,
+                                 std::forward_iterator_tag>);
+
+// When `iterator<false>::iterator_category` is `input_iterator_tag`
+static_assert(IteratorCategoryIs<View<ForwardIteratorWithInputCategory, forward_iterator>,
+                                 Pattern<forward_iterator>,
+                                 std::input_iterator_tag>);
+static_assert(IteratorCategoryIs<View<forward_iterator, ForwardIteratorWithInputCategory>,
+                                 Pattern<forward_iterator>,
+                                 std::input_iterator_tag>);
+static_assert(IteratorCategoryIs<View<forward_iterator, forward_iterator>,
+                                 Pattern<ForwardIteratorWithInputCategory>,
+                                 std::input_iterator_tag>);
+static_assert(IteratorCategoryIs<View<ForwardIteratorWithInputCategory, ForwardIteratorWithInputCategory>,
+                                 Pattern<ForwardIteratorWithInputCategory>,
+                                 std::input_iterator_tag>);
+
+template <class V, class Pattern>
+using ConstIteratorCategory = std::ranges::iterator_t<const std::ranges::join_with_view<V, Pattern>>::iterator_category;
+
+template <class V, class Pattern>
+concept HasConstIteratorCategory = requires { typename ConstIteratorCategory<V, Pattern>; };
+
+template <class V, class Pat, class Category>
+concept ConstIteratorCategoryIs = std::same_as<ConstIteratorCategory<V, Pat>, Category>;
+
+// `iterator<true>::iterator_category` is not defined in those
+// cases because `join_with_view<V, Pattern>` cannot const-accessed
+static_assert(!HasConstIteratorCategory<View<cpp20_input_iterator, forward_iterator>, Pattern<forward_iterator>>);
+static_assert(!HasConstIteratorCategory<View<forward_iterator, cpp20_input_iterator>, Pattern<forward_iterator>>);
+static_assert(!HasConstIteratorCategory<View<forward_iterator, forward_iterator>, Pattern<cpp20_input_iterator>>);
+static_assert(!HasConstIteratorCategory<RvalueView<forward_iterator, forward_iterator>, Pattern<forward_iterator>>);
+static_assert(HasConstIteratorCategory<View<forward_iterator, forward_iterator>, Pattern<forward_iterator>>);
+
+// When
+//   is_reference_v<common_reference_t<iter_reference_t<InnerIter>,
+//                                     iter_reference_t<PatternIter>>>
+// has different values for `iterator<true>`
+static_assert(ConstIteratorCategoryIs<View<forward_iterator, forward_iterator>,
+                                      RvaluePattern<forward_iterator>,
+                                      std::input_iterator_tag>);
+
+// When `iterator<true>::iterator_category` is `bidirectional_iterator_tag`
+static_assert(ConstIteratorCategoryIs<MaybeCommonView<true, bidirectional_iterator, true, bidirectional_iterator>,
+                                      MaybeCommonPattern<true, bidirectional_iterator>,
+                                      std::bidirectional_iterator_tag>);
+static_assert(ConstIteratorCategoryIs<MaybeCommonView<false, bidirectional_iterator, true, bidirectional_iterator>,
+                                      MaybeCommonPattern<true, bidirectional_iterator>,
+                                      std::bidirectional_iterator_tag>);
+static_assert(ConstIteratorCategoryIs<
+              BasicVectorView<
+                  BasicVectorView<float, ViewProperties{.common = true}, forward_iterator, bidirectional_iterator>,
+                  ViewProperties{.common = true},
+                  forward_iterator,
+                  bidirectional_iterator>,
+              BasicVectorView<float, ViewProperties{.common = true}, forward_iterator, bidirectional_iterator>,
+              std::bidirectional_iterator_tag>);
+
+// When `iterator<true>::iterator_category` is `forward_iterator_tag`
+static_assert(ConstIteratorCategoryIs<MaybeCommonView<true, forward_iterator, true, bidirectional_iterator>,
+                                      MaybeCommonPattern<true, bidirectional_iterator>,
+                                      std::forward_iterator_tag>);
+static_assert(ConstIteratorCategoryIs<MaybeCommonView<true, bidirectional_iterator, true, forward_iterator>,
+                                      MaybeCommonPattern<true, bidirectional_iterator>,
+                                      std::forward_iterator_tag>);
+static_assert(ConstIteratorCategoryIs<MaybeCommonView<true, bidirectional_iterator, true, bidirectional_iterator>,
+                                      MaybeCommonPattern<true, forward_iterator>,
+                                      std::forward_iterator_tag>);
+static_assert(ConstIteratorCategoryIs<MaybeCommonView<true, bidirectional_iterator, false, bidirectional_iterator>,
+                                      MaybeCommonPattern<true, bidirectional_iterator>,
+                                      std::forward_iterator_tag>);
+static_assert(ConstIteratorCategoryIs<MaybeCommonView<true, bidirectional_iterator, true, bidirectional_iterator>,
+                                      MaybeCommonPattern<false, bidirectional_iterator>,
+                                      std::forward_iterator_tag>);
+static_assert(ConstIteratorCategoryIs<MaybeCommonView<false, forward_iterator, false, forward_iterator>,
+                                      MaybeCommonPattern<false, forward_iterator>,
+                                      std::forward_iterator_tag>);
+static_assert(
+    ConstIteratorCategoryIs<
+        BasicVectorView<BasicVectorView<float, ViewProperties{}, ForwardIteratorWithInputCategory, forward_iterator>,
+                        ViewProperties{},
+                        ForwardIteratorWithInputCategory,
+                        forward_iterator>,
+        BasicVectorView<float, ViewProperties{}, ForwardIteratorWithInputCategory, forward_iterator>,
+        std::forward_iterator_tag>);
+
+// When `iterator<true>::iterator_category` is `input_iterator_tag`
+static_assert(ConstIteratorCategoryIs<View<ForwardIteratorWithInputCategory, forward_iterator>,
+                                      Pattern<forward_iterator>,
+                                      std::input_iterator_tag>);
+static_assert(ConstIteratorCategoryIs<View<forward_iterator, ForwardIteratorWithInputCategory>,
+                                      Pattern<forward_iterator>,
+                                      std::input_iterator_tag>);
+static_assert(ConstIteratorCategoryIs<View<forward_iterator, forward_iterator>,
+                                      Pattern<ForwardIteratorWithInputCategory>,
+                                      std::input_iterator_tag>);
+static_assert(ConstIteratorCategoryIs<View<ForwardIteratorWithInputCategory, ForwardIteratorWithInputCategory>,
+                                      Pattern<ForwardIteratorWithInputCategory>,
+                                      std::input_iterator_tag>);
+static_assert(ConstIteratorCategoryIs<
+              BasicVectorView<
+                  BasicVectorView<float, ViewProperties{}, DefaultCtorInputIter, ForwardIteratorWithInputCategory>,
+                  ViewProperties{},
+                  DefaultCtorInputIter,
+                  ForwardIteratorWithInputCategory>,
+              BasicVectorView<float, ViewProperties{}, ForwardIteratorWithInputCategory>,
+              std::input_iterator_tag>);
+} // namespace test_iterator_category
+
+namespace test_value_type {
+template <class ValueType, class ConstValueType = ValueType>
+struct View : std::ranges::view_base {
+  struct InnerRange : std::ranges::view_base {
+    ValueType* begin();
+    ValueType* end();
+    ConstValueType* begin() const;
+    ConstValueType* end() const;
+  };
+
+  InnerRange* begin();
+  InnerRange* end();
+  const InnerRange* begin() const;
+  const InnerRange* end() const;
+};
+
+template <class ValueType, class ConstValueType = ValueType>
+using Pattern = View<ValueType, ConstValueType>::InnerRange;
+
+template <class V, class Pat>
+using IteratorValueType = std::ranges::iterator_t<std::ranges::join_with_view<V, Pat>>::value_type;
+
+template <class V, class Pat, class ValueType>
+concept IteratorValueTypeIs = std::same_as<IteratorValueType<V, Pat>, ValueType>;
+
+// Test that `iterator<false>::value_type` is equal to
+//   common_type_t<iter_value_t<InnerIter>, iter_value_t<PatternIter>>
+static_assert(IteratorValueTypeIs<View<int>, Pattern<int>, int>);
+static_assert(IteratorValueTypeIs<View<int>, Pattern<long>, long>);
+static_assert(IteratorValueTypeIs<View<long>, Pattern<int>, long>);
+static_assert(IteratorValueTypeIs<View<std::nullptr_t>, Pattern<void*>, void*>);
+static_assert(IteratorValueTypeIs<View<std::tuple<long, int>>, Pattern<std::tuple<int, long>>, std::tuple<long, long>>);
+
+template <class V, class Pat>
+using ConstIteratorValueType = std::ranges::iterator_t<const std::ranges::join_with_view<V, Pat>>::value_type;
+
+template <class V, class Pat, class ValueType>
+concept ConstIteratorValueTypeIs = std::same_as<ConstIteratorValueType<V, Pat>, ValueType>;
+
+// Test that `iterator<true>::value_type` is equal to
+//   common_type_t<iter_value_t<InnerIter>, iter_value_t<PatternIter>>
+static_assert(ConstIteratorValueTypeIs<View<int>, Pattern<int>, int>);
+static_assert(ConstIteratorValueTypeIs<View<int>, Pattern<long>, long>);
+static_assert(ConstIteratorValueTypeIs<View<long>, Pattern<int>, long>);
+static_assert(ConstIteratorValueTypeIs<View<std::nullptr_t>, Pattern<void*>, void*>);
+static_assert(
+    ConstIteratorValueTypeIs<View<std::tuple<long, int>>, Pattern<std::tuple<int, long>>, std::tuple<long, long>>);
+
+// Test value types of non-simple const ranges
+static_assert(ConstIteratorValueTypeIs<View<short, int>, Pattern<short, int>, int>);
+static_assert(ConstIteratorValueTypeIs<View<short, int>, Pattern<int, long>, long>);
+static_assert(ConstIteratorValueTypeIs<View<int, long>, Pattern<short, int>, long>);
+static_assert(ConstIteratorValueTypeIs<View<int, std::nullptr_t>, Pattern<int, void*>, void*>);
+static_assert(ConstIteratorValueTypeIs<View<std::tuple<long, int>, std::pair<long, int>>,
+                                       Pattern<std::tuple<int, long>, std::pair<int, long>>,
+                                       std::pair<long, long>>);
+} // namespace test_value_type
+
+namespace test_difference_type {
+template <class DifferenceType, class ValueType>
+struct Iter {
+  using value_type      = std::remove_const_t<ValueType>;
+  using difference_type = DifferenceType;
+
+  ValueType& operator*() const;
+  Iter& operator++();
+  Iter operator++(int);
+  bool operator==(const Iter&) const;
+};
+
+static_assert(std::forward_iterator<Iter<int, void*>>);
+
+template <class DifferenceType,
+          class InnerDifferenceType,
+          class ConstDifferenceType      = DifferenceType,
+          class InnerConstDifferenceType = InnerDifferenceType>
+struct View : std::ranges::view_base {
+  struct InnerRange : std::ranges::view_base {
+    Iter<InnerDifferenceType, float> begin();
+    Iter<InnerDifferenceType, float> end();
+    Iter<InnerConstDifferenceType, double> begin() const;
+    Iter<InnerConstDifferenceType, double> end() const;
+  };
+
+  Iter<DifferenceType, InnerRange> begin();
+  Iter<DifferenceType, InnerRange> end();
+  Iter<ConstDifferenceType, const InnerRange> begin() const;
+  Iter<ConstDifferenceType, const InnerRange> end() const;
+};
+
+template <class DifferenceType, class ConstDifferenceType = DifferenceType>
+struct Pattern : std::ranges::view_base {
+  Iter<DifferenceType, float> begin();
+  Iter<DifferenceType, float> end();
+  Iter<ConstDifferenceType, double> begin() const;
+  Iter<ConstDifferenceType, double> end() const;
+};
+
+template <class V, class Pat>
+using IteratorDifferenceType = std::ranges::iterator_t<std::ranges::join_with_view<V, Pat>>::difference_type;
+
+template <class V, class Pat, class DifferenceType>
+concept IteratorDifferenceTypeIs = std::same_as<IteratorDifferenceType<V, Pat>, DifferenceType>;
+
+// Test that `iterator<false>::difference_type` is equal to
+//   common_type_t<
+//       iter_difference_t<OuterIter>,
+//       iter_difference_t<InnerIter>,
+//       iter_difference_t<PatternIter>>
+static_assert(IteratorDifferenceTypeIs<View<int, int>, Pattern<int>, int>);
+static_assert(IteratorDifferenceTypeIs<View<signed char, signed char>, Pattern<signed char>, signed char>);
+static_assert(IteratorDifferenceTypeIs<View<short, short>, Pattern<short>, short>);
+static_assert(IteratorDifferenceTypeIs<View<signed char, short>, Pattern<short>, int>);
+static_assert(IteratorDifferenceTypeIs<View<signed char, short>, Pattern<int>, int>);
+static_assert(IteratorDifferenceTypeIs<View<long long, long>, Pattern<int>, long long>);
+static_assert(IteratorDifferenceTypeIs<View<long, long long>, Pattern<int>, long long>);
+
+template <class V, class Pat>
+using ConstIteratorDifferenceType = std::ranges::iterator_t<const std::ranges::join_with_view<V, Pat>>::difference_type;
+
+template <class V, class Pat, class DifferenceType>
+concept ConstIteratorDifferenceTypeIs = std::same_as<ConstIteratorDifferenceType<V, Pat>, DifferenceType>;
+
+// Test that `iterator<true>::difference_type` is equal to
+//   common_type_t<
+//       iter_difference_t<OuterIter>,
+//       iter_difference_t<InnerIter>,
+//       iter_difference_t<PatternIter>>
+static_assert(ConstIteratorDifferenceTypeIs<View<int, int>, Pattern<int>, int>);
+static_assert(ConstIteratorDifferenceTypeIs<View<signed char, signed char>, Pattern<signed char>, signed char>);
+static_assert(ConstIteratorDifferenceTypeIs<View<short, short>, Pattern<short>, short>);
+static_assert(ConstIteratorDifferenceTypeIs<View<signed char, short>, Pattern<short>, int>);
+static_assert(ConstIteratorDifferenceTypeIs<View<signed char, short>, Pattern<int>, int>);
+static_assert(ConstIteratorDifferenceTypeIs<View<long long, long>, Pattern<int>, long long>);
+static_assert(ConstIteratorDifferenceTypeIs<View<long, long long>, Pattern<int>, long long>);
+
+// Test difference types of non-simple const ranges
+static_assert(ConstIteratorDifferenceTypeIs<View<short, short, int, int>, Pattern<short, int>, int>);
+static_assert(
+    ConstIteratorDifferenceTypeIs<View<int, short, signed char, signed char>, Pattern<long, signed char>, signed char>);
+static_assert(ConstIteratorDifferenceTypeIs<View<long, long long, signed char, short>, Pattern<long, short>, int>);
+static_assert(ConstIteratorDifferenceTypeIs<View<short, short, long long, long>, Pattern<short, int>, long long>);
+static_assert(ConstIteratorDifferenceTypeIs<View<signed char, signed char, long, long long>,
+                                            Pattern<signed char, int>,
+                                            long long>);
+} // namespace test_difference_type
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.overview/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.overview/adaptor.pass.cpp
new file mode 100644
index 00000000000000..82469a146f00b2
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.overview/adaptor.pass.cpp
@@ -0,0 +1,359 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <ranges>
+
+// std::views::join_with_view
+
+#include <ranges>
+
+#include <span>
+#include <string_view>
+#include <utility>
+
+#include "test_iterators.h"
+
+template <class View, class T>
+concept CanBePiped = requires(View&& view, T&& t) {
+  { std::forward<View>(view) | std::forward<T>(t) };
+};
+
+struct Range : std::ranges::view_base {
+  using Iterator = forward_iterator<std::string_view*>;
+  using Sentinel = sentinel_wrapper<Iterator>;
+  constexpr explicit Range(std::string_view* b, std::string_view* e) : begin_(b), end_(e) {}
+  constexpr Iterator begin() const { return Iterator(begin_); }
+  constexpr Sentinel end() const { return Sentinel(Iterator(end_)); }
+
+private:
+  std::string_view* begin_;
+  std::string_view* end_;
+};
+
+struct Pattern : std::ranges::view_base {
+  using Iterator = forward_iterator<const char*>;
+  using Sentinel = sentinel_wrapper<Iterator>;
+  static constexpr std::string_view pat{", "};
+
+  constexpr Pattern() = default;
+  constexpr Iterator begin() const { return Iterator(pat.data()); }
+  constexpr Sentinel end() const { return Sentinel(Iterator(pat.data() + pat.size())); }
+};
+
+struct NonCopyablePattern : Pattern {
+  NonCopyablePattern(const NonCopyablePattern&) = delete;
+};
+
+template <typename View>
+constexpr void compareViews(View v, std::string_view list) {
+  auto b1 = v.begin();
+  auto e1 = v.end();
+  auto b2 = list.begin();
+  auto e2 = list.end();
+  for (; b1 != e1 && b2 != e2; ++b1, ++b2) {
+    assert(*b1 == *b2);
+  }
+  assert(b1 == e1);
+  assert(b2 == e2);
+}
+
+constexpr int absoluteValue(int x) { return x < 0 ? -x : x; }
+
+template <class T>
+constexpr const T&& asConstRvalue(T&& t) {
+  return static_cast<const T&&>(t);
+}
+
+constexpr void test_adaptor_with_pattern(std::span<std::string_view> buff) {
+  // Test `views::join_with(pattern)(v)`
+  {
+    using Result = std::ranges::join_with_view<Range, Pattern>;
+    const Range range(buff.data(), buff.data() + buff.size());
+    Pattern pattern;
+
+    {
+      // 'views::join_with(pattern)' - &&
+      std::same_as<Result> decltype(auto) result = std::views::join_with(pattern)(range);
+      compareViews(result, "abcd, ef, ghij, kl");
+    }
+    {
+      // 'views::join_with(pattern)' - const&&
+      std::same_as<Result> decltype(auto) result = asConstRvalue(std::views::join_with(pattern))(range);
+      compareViews(result, "abcd, ef, ghij, kl");
+    }
+    {
+      // 'views::join_with(pattern)' - &
+      auto partial                               = std::views::join_with(pattern);
+      std::same_as<Result> decltype(auto) result = partial(range);
+      compareViews(result, "abcd, ef, ghij, kl");
+    }
+    {
+      // 'views::join_with(pattern)' - const&
+      auto const partial                         = std::views::join_with(pattern);
+      std::same_as<Result> decltype(auto) result = partial(range);
+      compareViews(result, "abcd, ef, ghij, kl");
+    }
+  }
+
+  // Test `v | views::join_with(pattern)`
+  {
+    using Result = std::ranges::join_with_view<Range, Pattern>;
+    const Range range(buff.data(), buff.data() + buff.size());
+    Pattern pattern;
+
+    {
+      // 'views::join_with(pattern)' - &&
+      std::same_as<Result> decltype(auto) result = range | std::views::join_with(pattern);
+      compareViews(result, "abcd, ef, ghij, kl");
+    }
+    {
+      // 'views::join_with(pattern)' - const&&
+      std::same_as<Result> decltype(auto) result = range | asConstRvalue(std::views::join_with(pattern));
+      compareViews(result, "abcd, ef, ghij, kl");
+    }
+    {
+      // 'views::join_with(pattern)' - &
+      auto partial                               = std::views::join_with(pattern);
+      std::same_as<Result> decltype(auto) result = range | partial;
+      compareViews(result, "abcd, ef, ghij, kl");
+    }
+    {
+      // 'views::join_with(pattern)' - const&
+      auto const partial                         = std::views::join_with(pattern);
+      std::same_as<Result> decltype(auto) result = range | partial;
+      compareViews(result, "abcd, ef, ghij, kl");
+    }
+  }
+
+  // Test `views::join_with(v, pattern)` range adaptor object
+  {
+    using Result = std::ranges::join_with_view<Range, Pattern>;
+    const Range range(buff.data(), buff.data() + buff.size());
+    Pattern pattern;
+
+    {
+      // 'views::join_with' - &&
+      auto range_adaptor                         = std::views::join_with;
+      std::same_as<Result> decltype(auto) result = std::move(range_adaptor)(range, pattern);
+      compareViews(result, "abcd, ef, ghij, kl");
+    }
+    {
+      // 'views::join_with' - const&&
+      const auto range_adaptor                   = std::views::join_with;
+      std::same_as<Result> decltype(auto) result = std::move(range_adaptor)(range, pattern);
+      compareViews(result, "abcd, ef, ghij, kl");
+    }
+    {
+      // 'views::join_with' - &
+      auto range_adaptor                         = std::views::join_with;
+      std::same_as<Result> decltype(auto) result = range_adaptor(range, pattern);
+      compareViews(result, "abcd, ef, ghij, kl");
+    }
+    {
+      // 'views::join_with' - const&
+      const auto range_adaptor                   = std::views::join_with;
+      std::same_as<Result> decltype(auto) result = range_adaptor(range, pattern);
+      compareViews(result, "abcd, ef, ghij, kl");
+    }
+  }
+
+  // Test `adaptor | views::join_with(pattern)`
+  {
+    auto pred    = [](std::string_view s) { return s.size() >= 3; };
+    using Result = std::ranges::join_with_view<std::ranges::filter_view<Range, decltype(pred)>, Pattern>;
+    const Range range(buff.data(), buff.data() + buff.size());
+    Pattern pattern;
+
+    {
+      std::same_as<Result> decltype(auto) result = range | std::views::filter(pred) | std::views::join_with(pattern);
+      compareViews(result, "abcd, ghij");
+    }
+    {
+      const auto partial                         = std::views::filter(pred) | std::views::join_with(pattern);
+      std::same_as<Result> decltype(auto) result = range | partial;
+      compareViews(result, "abcd, ghij");
+    }
+  }
+}
+
+constexpr void test_adaptor_with_single_element(std::span<std::string_view> buff) {
+  // Test `views::join_with(element)(v)`
+  {
+    using Result = std::ranges::join_with_view<Range, std::ranges::single_view<char>>;
+    const Range range(buff.data(), buff.data() + buff.size());
+    const char element = '.';
+
+    {
+      // 'views::join_with(element)' - &&
+      std::same_as<Result> decltype(auto) result = std::views::join_with(element)(range);
+      compareViews(result, "abcd.ef.ghij.kl");
+    }
+    {
+      // 'views::join_with(element)' - const&&
+      std::same_as<Result> decltype(auto) result = asConstRvalue(std::views::join_with(element))(range);
+      compareViews(result, "abcd.ef.ghij.kl");
+    }
+    {
+      // 'views::join_with(element)' - &
+      auto partial                               = std::views::join_with(element);
+      std::same_as<Result> decltype(auto) result = partial(range);
+      compareViews(result, "abcd.ef.ghij.kl");
+    }
+    {
+      // 'views::join_with(element)' - const&
+      const auto partial                         = std::views::join_with(element);
+      std::same_as<Result> decltype(auto) result = partial(range);
+      compareViews(result, "abcd.ef.ghij.kl");
+    }
+  }
+
+  // Test `v | views::join_with(element)`
+  {
+    using Result = std::ranges::join_with_view<Range, std::ranges::single_view<char>>;
+    const Range range(buff.data(), buff.data() + buff.size());
+    const char element = '.';
+
+    {
+      // 'views::join_with(element)' - &&
+      std::same_as<Result> decltype(auto) result = range | std::views::join_with(element);
+      compareViews(result, "abcd.ef.ghij.kl");
+    }
+    {
+      // 'views::join_with(element)' - const&&
+      std::same_as<Result> decltype(auto) result = range | asConstRvalue(std::views::join_with(element));
+      compareViews(result, "abcd.ef.ghij.kl");
+    }
+    {
+      // 'views::join_with(element)' - &
+      auto partial                               = std::views::join_with(element);
+      std::same_as<Result> decltype(auto) result = range | partial;
+      compareViews(result, "abcd.ef.ghij.kl");
+    }
+    {
+      // 'views::join_with(element)' - const&
+      const auto partial                         = std::views::join_with(element);
+      std::same_as<Result> decltype(auto) result = range | partial;
+      compareViews(result, "abcd.ef.ghij.kl");
+    }
+  }
+
+  // Test `views::join_with(v, element)` range adaptor object
+  {
+    using Result = std::ranges::join_with_view<Range, std::ranges::single_view<char>>;
+    const Range range(buff.data(), buff.data() + buff.size());
+    const char element = '.';
+
+    {
+      // 'views::join_with' - &&
+      auto range_adaptor                         = std::views::join_with;
+      std::same_as<Result> decltype(auto) result = std::move(range_adaptor)(range, element);
+      compareViews(result, "abcd.ef.ghij.kl");
+    }
+    {
+      // 'views::join_with' - const&&
+      const auto range_adaptor                   = std::views::join_with;
+      std::same_as<Result> decltype(auto) result = std::move(range_adaptor)(range, element);
+      compareViews(result, "abcd.ef.ghij.kl");
+    }
+    {
+      // 'views::join_with' - &
+      auto range_adaptor                         = std::views::join_with;
+      std::same_as<Result> decltype(auto) result = range_adaptor(range, element);
+      compareViews(result, "abcd.ef.ghij.kl");
+    }
+    {
+      // 'views::join_with' - const&
+      const auto range_adaptor                   = std::views::join_with;
+      std::same_as<Result> decltype(auto) result = range_adaptor(range, element);
+      compareViews(result, "abcd.ef.ghij.kl");
+    }
+  }
+
+  // Test `adaptor | views::join_with(element)`
+  {
+    auto pred = [](std::string_view s) { return s.size() >= 3; };
+    using Result =
+        std::ranges::join_with_view<std::ranges::filter_view<Range, decltype(pred)>, std::ranges::single_view<char>>;
+    const Range range(buff.data(), buff.data() + buff.size());
+    const char element = '.';
+
+    {
+      std::same_as<Result> decltype(auto) result = range | std::views::filter(pred) | std::views::join_with(element);
+      compareViews(result, "abcd.ghij");
+    }
+    {
+      const auto partial                         = std::views::filter(pred) | std::views::join_with(element);
+      std::same_as<Result> decltype(auto) result = range | partial;
+      compareViews(result, "abcd.ghij");
+    }
+  }
+}
+
+constexpr bool test() {
+  std::string_view buff[] = {"abcd", "ef", "ghij", "kl"};
+
+  // Test range adaptor object
+  {
+    using RangeAdaptorObject = decltype(std::views::join_with);
+    static_assert(std::is_const_v<RangeAdaptorObject>);
+
+    // The type of a customization point object, ignoring cv-qualifiers, shall model semiregular
+    static_assert(std::semiregular<std::remove_const<RangeAdaptorObject>>);
+  }
+
+  test_adaptor_with_pattern(buff);
+  test_adaptor_with_single_element(buff);
+
+  // Test that one can call std::views::join_with with arbitrary stuff, as long as we
+  // don't try to actually complete the call by passing it a range.
+  //
+  // That makes no sense and we can't do anything with the result, but it's valid.
+  {
+    long array[3]                 = {1, 2, 3};
+    [[maybe_unused]] auto partial = std::views::join_with(std::move(array));
+  }
+
+  // Test SFINAE friendliness
+  {
+    struct NotAView {};
+
+    static_assert(!CanBePiped<Range, decltype(std::views::join_with)>);
+    static_assert(CanBePiped<Range, decltype(std::views::join_with(Pattern{}))>);
+    static_assert(CanBePiped<Range, decltype(std::views::join_with('.'))>);
+    static_assert(!CanBePiped<NotAView, decltype(std::views::join_with(Pattern{}))>);
+    static_assert(!CanBePiped<NotAView, decltype(std::views::join_with('.'))>);
+    static_assert(!CanBePiped<std::initializer_list<char>, decltype(std::views::join_with(Pattern{}))>);
+    static_assert(!CanBePiped<std::initializer_list<char>, decltype(std::views::join_with('.'))>);
+    static_assert(!CanBePiped<Range, decltype(std::views::join_with(NotAView{}))>);
+
+    static_assert(!std::is_invocable_v<decltype(std::views::join_with)>);
+    static_assert(!std::is_invocable_v<decltype(std::views::join_with), Pattern, Range>);
+    static_assert(!std::is_invocable_v<decltype(std::views::join_with), char, Range>);
+    static_assert(std::is_invocable_v<decltype(std::views::join_with), Range, Pattern>);
+    static_assert(std::is_invocable_v<decltype(std::views::join_with), Range, char>);
+    static_assert(!std::is_invocable_v<decltype(std::views::join_with), Range, Pattern, Pattern>);
+    static_assert(!std::is_invocable_v<decltype(std::views::join_with), Range, char, char>);
+    static_assert(!std::is_invocable_v<decltype(std::views::join_with), NonCopyablePattern>);
+  }
+
+  {
+    static_assert(std::is_same_v<decltype(std::ranges::views::join_with), decltype(std::views::join_with)>);
+    assert(std::addressof(std::ranges::views::join_with) == std::addressof(std::views::join_with));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.overview/example.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.overview/example.pass.cpp
new file mode 100644
index 00000000000000..e2409c67327e91
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.overview/example.pass.cpp
@@ -0,0 +1,45 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <ranges>
+
+// [Example 1:
+//   vector<string> vs = {"the", "quick", "brown", "fox"};
+//   for (char c : vs | views::join_with('-')) {
+//     cout << c;
+//   }
+//   // The above prints the-quick-brown-fox
+// - end example]
+
+#include <ranges>
+
+#include <algorithm>
+#include <string>
+#include <string_view>
+#include <vector>
+
+using namespace std::string_view_literals;
+
+constexpr bool test() {
+  std::vector<std::string> vs = {"the", "quick", "brown", "fox"};
+  std::string result;
+  for (char c : vs | std::views::join_with('-')) {
+    result += c;
+  }
+
+  return std::ranges::equal(result, "the-quick-brown-fox"sv);
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.sentinel/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.sentinel/ctor.default.pass.cpp
new file mode 100644
index 00000000000000..6b4d21f080cba9
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.sentinel/ctor.default.pass.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <ranges>
+
+// sentinel() = default;
+
+#include <ranges>
+
+#include "../types.h"
+
+constexpr bool test() {
+  using Inner   = BasicVectorView<char, ViewProperties{.common = false}, forward_iterator>;
+  using V       = BasicVectorView<Inner, ViewProperties{}, forward_iterator>;
+  using Pattern = Inner;
+  using JWV     = std::ranges::join_with_view<V, Pattern>;
+  static_assert(!std::ranges::common_range<JWV>);
+
+  [[maybe_unused]] std::ranges::sentinel_t<JWV> se;
+  [[maybe_unused]] std::ranges::sentinel_t<const JWV> cse;
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.sentinel/ctor.non_const.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.sentinel/ctor.non_const.pass.cpp
new file mode 100644
index 00000000000000..7841b4a4ba3f60
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.sentinel/ctor.non_const.pass.cpp
@@ -0,0 +1,71 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <ranges>
+
+// constexpr sentinel(sentinel<!Const> s)
+//   requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>;
+
+#include <ranges>
+
+#include <type_traits>
+#include <vector>
+
+#include "../types.h"
+#include "test_iterators.h"
+
+constexpr bool test() {
+  { // Regular conversion from `!Const` to `Const` sentinel
+    using Inner            = BasicVectorView<int, ViewProperties{.common = false}, forward_iterator>;
+    std::vector<Inner> vec = {Inner{11, 12}, Inner{13, 14}};
+
+    std::ranges::join_with_view jwv(vec, 0);
+    using JWV = decltype(jwv);
+    static_assert(!std::ranges::common_range<JWV>);
+    using CSent = std::ranges::sentinel_t<const JWV>;
+
+    auto se                    = jwv.end();
+    [[maybe_unused]] CSent cse = se;
+  }
+
+  { // Test conversion from `Const` to `!Const` (should be invalid)
+    using Inner   = BasicVectorView<int, ViewProperties{.common = false}, forward_iterator>;
+    using V       = std::vector<Inner>;
+    using Pattern = std::ranges::single_view<int>;
+    using JWV     = std::ranges::join_with_view<std::views::all_t<V>, Pattern>;
+    static_assert(!std::ranges::common_range<JWV>);
+
+    using Sent  = std::ranges::sentinel_t<JWV>;
+    using CSent = std::ranges::sentinel_t<const JWV>;
+    static_assert(!std::convertible_to<CSent, Sent>);
+    static_assert(!std::constructible_from<Sent, CSent>);
+  }
+
+  { // When `convertible_to<sentinel_t<V>, sentinel_t<Base>>` is not modeled
+    using V       = ConstOppositeView<std::vector<long>>;
+    using Pattern = std::ranges::single_view<long>;
+    using JWV     = std::ranges::join_with_view<V, Pattern>;
+    static_assert(!std::ranges::common_range<JWV>);
+
+    using Sent  = std::ranges::sentinel_t<JWV>;
+    using CSent = std::ranges::sentinel_t<const JWV>;
+    static_assert(!std::convertible_to<CSent, Sent>);
+    static_assert(!std::constructible_from<Sent, CSent>);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.sentinel/eq.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.sentinel/eq.pass.cpp
new file mode 100644
index 00000000000000..6e39cf153bae30
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.sentinel/eq.pass.cpp
@@ -0,0 +1,101 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <ranges>
+
+// template<bool OtherConst>
+//   requires sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>>
+// friend constexpr bool operator==(const iterator<OtherConst>& x, const sentinel& y);
+
+#include <ranges>
+
+#include <cassert>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include "../types.h"
+#include "test_comparisons.h"
+#include "test_iterators.h"
+
+struct NonCrossConstComparableView : std::ranges::view_base {
+  using NonConstRange = std::vector<int>;
+  NonConstRange* begin();
+  sentinel_wrapper<NonConstRange*> end();
+
+  using ConstRange = BasicVectorView<int, ViewProperties{}, forward_iterator>;
+  ConstRange* begin() const;
+  sentinel_wrapper<ConstRange*> end() const;
+};
+
+static_assert(std::ranges::range<NonCrossConstComparableView>);
+static_assert(std::ranges::range<const NonCrossConstComparableView>);
+
+constexpr bool test() {
+  using Inner   = BasicVectorView<int, ViewProperties{.common = false}, cpp20_input_iterator>;
+  using V       = std::vector<Inner>;
+  using Pattern = std::ranges::single_view<int>;
+  using JWV     = std::ranges::join_with_view<std::ranges::owning_view<V>, Pattern>;
+  static_assert(!std::ranges::common_range<JWV>);
+
+  {   // Compare iterator<Const> with sentinel<Const>
+    { // Const == true
+      AssertEqualityReturnBool<std::ranges::iterator_t<const JWV>, std::ranges::sentinel_t<const JWV>>();
+      const JWV jwv(V{Inner{1, 2}, Inner{4}}, 3);
+      assert(testEquality(std::ranges::next(jwv.begin(), 4), jwv.end(), true));
+      assert(testEquality(jwv.begin(), jwv.end(), false));
+    }
+
+    { // Const == false
+      AssertEqualityReturnBool<std::ranges::iterator_t<JWV>, std::ranges::sentinel_t<JWV>>();
+      JWV jwv(V{Inner{5}, Inner{7, 8}}, 6);
+      assert(testEquality(std::ranges::next(jwv.begin(), 4), jwv.end(), true));
+      assert(testEquality(std::ranges::next(jwv.begin(), 2), jwv.end(), false));
+    }
+  }
+
+  {   // Compare iterator<Const> with sentinel<!Const>
+    { // Const == true
+      AssertEqualityReturnBool<std::ranges::iterator_t<const JWV>, std::ranges::sentinel_t<JWV>>();
+      JWV jwv(V{Inner{9, 10}, Inner{12}}, 11);
+      assert(testEquality(std::ranges::next(std::as_const(jwv).begin(), 4), jwv.end(), true));
+      assert(testEquality(std::ranges::next(std::as_const(jwv).begin(), 2), jwv.end(), false));
+    }
+
+    { // Const == false
+      AssertEqualityReturnBool<std::ranges::iterator_t<JWV>, std::ranges::sentinel_t<const JWV>>();
+      JWV jwv(V{Inner{13}, Inner{15, 16}}, 14);
+      assert(testEquality(std::ranges::next(jwv.begin(), 4), std::as_const(jwv).end(), true));
+      assert(testEquality(std::ranges::next(jwv.begin(), 3), std::as_const(jwv).end(), false));
+    }
+  }
+
+  { // Check invalid comparisons between iterator<Const> and sentinel<!Const>
+    using JWV2 = std::ranges::join_with_view<NonCrossConstComparableView, Pattern>;
+    static_assert(!std::ranges::common_range<JWV2>);
+
+    static_assert(!weakly_equality_comparable_with<std::ranges::iterator_t<const JWV2>, std::ranges::sentinel_t<JWV2>>);
+    static_assert(!weakly_equality_comparable_with<std::ranges::iterator_t<JWV2>, std::ranges::sentinel_t<const JWV2>>);
+
+    // Those should be valid
+    static_assert(weakly_equality_comparable_with<std::ranges::iterator_t<JWV2>, std::ranges::sentinel_t<JWV2>>);
+    static_assert(
+        weakly_equality_comparable_with<std::ranges::iterator_t<const JWV2>, std::ranges::sentinel_t<const JWV2>>);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/base.pass.cpp
new file mode 100644
index 00000000000000..eb6731443283d0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/base.pass.cpp
@@ -0,0 +1,148 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <ranges>
+
+// constexpr View base() const& requires copy_constructible<View>;
+// constexpr View base() &&;
+
+#include <ranges>
+
+#include <cassert>
+#include <utility>
+#include <vector>
+
+using InnerRange = std::vector<int>;
+
+struct Range : std::ranges::view_base {
+  constexpr explicit Range(InnerRange* b, InnerRange* e) : begin_(b), end_(e) {}
+  constexpr Range(const Range& other) : begin_(other.begin_), end_(other.end_), was_copy_initialized_(true) {}
+  constexpr Range(Range&& other) : begin_(other.begin_), end_(other.end_), was_move_initialized_(true) {}
+  Range& operator=(const Range&) = default;
+  Range& operator=(Range&&)      = default;
+  constexpr InnerRange* begin() const { return begin_; }
+  constexpr InnerRange* end() const { return end_; }
+
+  InnerRange* begin_;
+  InnerRange* end_;
+  bool was_copy_initialized_ = false;
+  bool was_move_initialized_ = false;
+};
+
+static_assert(std::ranges::view<Range>);
+static_assert(std::ranges::input_range<Range>);
+
+struct Pattern : std::ranges::view_base {
+  static constexpr int pat[2] = {0, 0};
+  constexpr const int* begin() const { return pat; }
+  constexpr const int* end() const { return pat + 2; }
+};
+
+static_assert(std::ranges::view<Pattern>);
+static_assert(std::ranges::forward_range<Pattern>);
+
+template <class Tp>
+struct NonCopyableRange : std::ranges::view_base {
+  NonCopyableRange(const NonCopyableRange&)            = delete;
+  NonCopyableRange(NonCopyableRange&&)                 = default;
+  NonCopyableRange& operator=(const NonCopyableRange&) = default;
+  NonCopyableRange& operator=(NonCopyableRange&&)      = default;
+  Tp* begin() const;
+  Tp* end() const;
+};
+
+static_assert(!std::copy_constructible<NonCopyableRange<InnerRange>>);
+static_assert(!std::copy_constructible<NonCopyableRange<int>>);
+
+template <typename T>
+concept CanCallBaseOn = requires(T&& t) { std::forward<T>(t).base(); };
+
+constexpr bool test() {
+  InnerRange buff[3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
+  Pattern pattern;
+
+  // Check the const& overload
+  {
+    Range range(buff, buff + 3);
+    std::ranges::join_with_view<Range, Pattern> view(range, pattern);
+    std::same_as<Range> decltype(auto) result = view.base();
+    assert(result.was_copy_initialized_);
+    assert(result.begin() == buff);
+    assert(result.end() == buff + 3);
+  }
+
+  // Check the const& overload on const `view`
+  {
+    Range range(buff, buff + 3);
+    const std::ranges::join_with_view<Range, Pattern> view(range, pattern);
+    std::same_as<Range> decltype(auto) result = view.base();
+    assert(result.was_copy_initialized_);
+    assert(result.begin() == buff);
+    assert(result.end() == buff + 3);
+  }
+
+  // Check the && overload
+  {
+    Range range(buff, buff + 3);
+    std::ranges::join_with_view<Range, Pattern> view(range, pattern);
+    std::same_as<Range> decltype(auto) result = std::move(view).base();
+    assert(result.was_move_initialized_);
+    assert(result.begin() == buff);
+    assert(result.end() == buff + 3);
+  }
+
+  // Check the && overload on const `view`
+  {
+    Range range(buff, buff + 3);
+    const std::ranges::join_with_view<Range, Pattern> view(range, pattern);
+    std::same_as<Range> decltype(auto) result = std::move(view).base();
+    assert(result.was_copy_initialized_);
+    assert(result.begin() == buff);
+    assert(result.end() == buff + 3);
+  }
+
+  // Ensure the const& overload is not considered when the base is not copy-constructible
+  {
+    static_assert(!CanCallBaseOn<const std::ranges::join_with_view<NonCopyableRange<InnerRange>, Pattern>&>);
+    static_assert(!CanCallBaseOn<std::ranges::join_with_view<NonCopyableRange<InnerRange>, Pattern>&>);
+    static_assert(!CanCallBaseOn<const std::ranges::join_with_view<NonCopyableRange<InnerRange>, Pattern>&&>);
+    static_assert(CanCallBaseOn<std::ranges::join_with_view<NonCopyableRange<InnerRange>, Pattern>&&>);
+    static_assert(CanCallBaseOn<std::ranges::join_with_view<NonCopyableRange<InnerRange>, Pattern>>);
+  }
+
+  // Ensure the const& overload does not depend on Pattern's copy-constructability
+  {
+    static_assert(CanCallBaseOn<const std::ranges::join_with_view<Range, NonCopyableRange<int>>& >);
+    static_assert(CanCallBaseOn<std::ranges::join_with_view<Range, NonCopyableRange<int>>& >);
+    static_assert(CanCallBaseOn<const std::ranges::join_with_view<Range, NonCopyableRange<int>>&& >);
+    static_assert(CanCallBaseOn<std::ranges::join_with_view<Range, NonCopyableRange<int>>&& >);
+    static_assert(CanCallBaseOn<std::ranges::join_with_view<Range, NonCopyableRange<int>>>);
+  }
+
+  // Check above two at the same time
+  {
+    static_assert(
+        !CanCallBaseOn<const std::ranges::join_with_view<NonCopyableRange<InnerRange>, NonCopyableRange<int>>&>);
+    static_assert(!CanCallBaseOn<std::ranges::join_with_view<NonCopyableRange<InnerRange>, NonCopyableRange<int>>&>);
+    static_assert(
+        !CanCallBaseOn<const std::ranges::join_with_view< NonCopyableRange<InnerRange>, NonCopyableRange<int>>&&>);
+    static_assert(CanCallBaseOn<std::ranges::join_with_view<NonCopyableRange<InnerRange>, NonCopyableRange<int>>&&>);
+    static_assert(CanCallBaseOn<std::ranges::join_with_view<NonCopyableRange<InnerRange>, NonCopyableRange<int>>>);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/begin.pass.cpp
new file mode 100644
index 00000000000000..98b6775146dd35
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/begin.pass.cpp
@@ -0,0 +1,257 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <ranges>
+
+// constexpr auto begin() {
+//   if constexpr (forward_range<V>) {
+//     constexpr bool use_const =
+//       simple-view<V> && is_reference_v<InnerRng> && simple-view<Pattern>;
+//     return iterator<use_const>{*this, ranges::begin(base_)};
+//   }
+//   else {
+//     outer_it_ = ranges::begin(base_);
+//     return iterator<false>{*this};
+//   }
+// }
+// constexpr auto begin() const
+//   requires forward_range<const V> &&
+//            forward_range<const Pattern> &&
+//            is_reference_v<range_reference_t<const V>> &&
+//            input_range<range_reference_t<const V>> {
+//   return iterator<true>{*this, ranges::begin(base_)};
+// }
+
+#include <ranges>
+
+#include <algorithm>
+#include <array>
+#include <string>
+#include <vector>
+
+#include "../types.h"
+#include "test_iterators.h"
+
+template <bool Simple>
+using MaybeSimpleForwardView = BasicView<std::vector<std::string>, ViewProperties{.simple = Simple}, forward_iterator>;
+
+template <bool Simple>
+using MaybeSimpleForwardRvalueView =
+    BasicView<RvalueVector<std::string>, ViewProperties{.simple = Simple}, forward_iterator>;
+
+template <bool Simple>
+using MaybeSimplePattern = BasicView<std::string, ViewProperties{.simple = Simple}, forward_iterator>;
+
+class DifferentWhenConstV {
+  using Arr                      = std::array<std::array<int, 2>, 2>;
+  static constexpr Arr regular_v = {{{1, 2}, {3, 4}}};
+  static constexpr Arr const_v   = {{{5, 6}, {7, 8}}};
+
+public:
+  constexpr auto begin() { return regular_v.begin(); }
+  constexpr auto end() { return regular_v.end(); }
+  constexpr auto begin() const { return const_v.begin(); }
+  constexpr auto end() const { return const_v.end(); }
+};
+
+class DifferentWhenConstPattern {
+  using Pat                            = std::array<int, 2>;
+  static constexpr Pat regular_pattern = {0, 0};
+  static constexpr Pat const_pattern   = {1, 1};
+
+public:
+  constexpr auto begin() { return regular_pattern.begin(); }
+  constexpr auto end() { return regular_pattern.end(); }
+  constexpr auto begin() const { return const_pattern.begin(); }
+  constexpr auto end() const { return const_pattern.end(); }
+};
+
+template <class V, class Pattern>
+concept JoinWithViewHasConstBegin = requires(const std::ranges::join_with_view<V, Pattern> jwv) {
+  { jwv.begin() } -> std::input_iterator;
+};
+
+constexpr void test_begin() {
+  using Str = std::string;
+  using Vec = std::vector<Str>;
+
+  { // `V` models simple-view
+    // `is_reference_v<InnerRng>` is true
+    // `Pattern` models simple-view
+    // `V` and `Pattern` contain some elements
+    using V       = MaybeSimpleForwardView<true>;
+    using Pattern = MaybeSimplePattern<true>;
+    std::ranges::join_with_view<V, Pattern> jwv(V(Vec{"A", "B", "C"}), Pattern(Str{">>"}));
+    auto it = jwv.begin();
+    assert(std::ranges::equal(std::views::counted(it, 7), Str{"A>>B>>C"}));
+  }
+
+  { // `V` does not model simple-view
+    // `is_reference_v<InnerRng>` is true
+    // `Pattern` models simple-view
+    // `V` and `Pattern` are empty
+    using V       = MaybeSimpleForwardView<false>;
+    using Pattern = MaybeSimplePattern<true>;
+    std::ranges::join_with_view<V, Pattern> jwv(V(Vec{}), Pattern(Str{}));
+    auto it = jwv.begin();
+    assert(it == jwv.end());
+  }
+
+  { // `V` models simple-view
+    // `is_reference_v<InnerRng>` is false
+    // `Pattern` models simple-view
+    // `V` contains two elements, `Pattern` is empty
+    using V       = MaybeSimpleForwardRvalueView<true>;
+    using Pattern = MaybeSimplePattern<true>;
+    std::ranges::join_with_view<V, Pattern> jwv(V(Vec{"1", "2"}), Pattern(Str{""}));
+    auto it = jwv.begin();
+    assert(*it == '1');
+    assert(*++it == '2');
+  }
+
+  { // `V` models simple-view
+    // `is_reference_v<InnerRng>` is true
+    // `Pattern` does not model simple-view
+    // `V` contains one element, `Pattern` is empty
+    using V       = MaybeSimpleForwardView<true>;
+    using Pattern = MaybeSimplePattern<false>;
+    std::ranges::join_with_view<V, Pattern> jwv(V(Vec{"07"}), Pattern(Str{}));
+    auto it = jwv.begin();
+    assert(*it++ == '0');
+    assert(*it == '7');
+  }
+
+  { // `V` does not model simple-view
+    // `is_reference_v<InnerRng>` is false
+    // `Pattern` models simple-view
+    // `V` contains three elements (2nd is empty), `Pattern` is not empty
+    using V       = MaybeSimpleForwardRvalueView<false>;
+    using Pattern = MaybeSimplePattern<true>;
+    std::ranges::join_with_view<V, Pattern> jwv(V(Vec{"A", "", "C"}), Pattern(Str{"--"}));
+    auto it = jwv.begin();
+    assert(std::ranges::equal(std::views::counted(it, 6), Str("A----C")));
+  }
+
+  { // `V` does not model simple-view
+    // `is_reference_v<InnerRng>` is true
+    // `Pattern` does not model simple-view
+    // `V` contains some empty elements, `Pattern` is not empty
+    using V       = MaybeSimpleForwardView<false>;
+    using Pattern = MaybeSimplePattern<false>;
+    std::ranges::join_with_view<V, Pattern> jwv(V(Vec{"", "", ""}), Pattern(Str{"-"}));
+    auto it = jwv.begin();
+    assert(*it++ == '-');
+    assert(*it == '-');
+  }
+
+  { // `V` models simple-view
+    // `is_reference_v<InnerRng>` is false
+    // `Pattern` does not model simple-view
+    // `V` contains two elements, `Pattern` is not empty
+    using V       = MaybeSimpleForwardRvalueView<true>;
+    using Pattern = MaybeSimplePattern<false>;
+    std::ranges::join_with_view<V, Pattern> jwv(V(Vec{"X", "Z"}), Pattern(Str{"Y"}));
+    auto it = jwv.begin();
+    assert(*it == 'X');
+    assert(*++it == 'Y');
+    assert(*++it == 'Z');
+  }
+
+  { // `V` does not model simple-view
+    // `is_reference_v<InnerRng>` is false
+    // `Pattern` does not model simple-view
+    // `V` contains two empty elements, `Pattern` is not empty
+    using V       = MaybeSimpleForwardRvalueView<false>;
+    using Pattern = MaybeSimplePattern<false>;
+    std::ranges::join_with_view<V, Pattern> jwv(V(Vec{"", ""}), Pattern(Str{"?"}));
+    auto it = jwv.begin();
+    assert(*it == '?');
+    assert(++it == jwv.end());
+  }
+
+  { // `V` does not model forward range
+    // `V` contains some empty elements, `Pattern` is empty
+    using V       = BasicView<Vec, ViewProperties{.common = false}, cpp20_input_iterator>;
+    using Pattern = MaybeSimplePattern<false>;
+    std::ranges::join_with_view<V, Pattern> jwv(V(Vec{"", "", ""}), Pattern(Str{""}));
+    auto it = jwv.begin();
+    assert(it == jwv.end());
+  }
+}
+
+constexpr void test_const_begin() {
+  { // `const V` models forward range
+    // `const Pattern` models forward range
+    // `is_reference_v<range_reference_t<const V>>` is true
+    // `range_reference_t<const V>` models input range
+    using V       = BasicView<DifferentWhenConstV, ViewProperties{}, forward_iterator>;
+    using Pattern = BasicView<DifferentWhenConstPattern, ViewProperties{}, forward_iterator>;
+
+    const std::ranges::join_with_view<V, Pattern> jwv;
+    auto it = jwv.begin();
+    assert(std::ranges::equal(std::views::counted(it, 6), std::array{5, 6, 1, 1, 7, 8}));
+  }
+
+  using Vec = std::vector<std::array<int, 2>>;
+  using Pat = std::array<int, 2>;
+
+  { // `const V` does not model forward range
+    // `const Pattern` models forward range
+    // `is_reference_v<range_reference_t<const V>>` is true
+    // `range_reference_t<const V>` models input range
+    static_assert(!JoinWithViewHasConstBegin<BasicView<Vec, ViewProperties{.common = false}, cpp20_input_iterator>,
+                                             BasicView<Pat, ViewProperties{}, forward_iterator>>);
+  }
+
+  { // `const V` models forward range
+    // `const Pattern` does not model forward range
+    // `is_reference_v<range_reference_t<const V>>` is true
+    // `range_reference_t<const V>` models input range
+    static_assert(!JoinWithViewHasConstBegin<BasicView<Vec, ViewProperties{}, forward_iterator>,
+                                             BasicView<Pat, ViewProperties{.common = false}, cpp20_input_iterator>>);
+  }
+
+  { // `const V` models forward range
+    // `const Pattern` models forward range
+    // `is_reference_v<range_reference_t<const V>>` is false
+    // `range_reference_t<const V>` models input range
+    static_assert(
+        !JoinWithViewHasConstBegin<BasicView<RvalueVector<std::vector<int>>, ViewProperties{}, forward_iterator>,
+                                   BasicView<Pat, ViewProperties{}, forward_iterator>>);
+  }
+
+  { // `const V` models forward range
+    // `const Pattern` models forward range
+    // `is_reference_v<range_reference_t<const V>>` is true
+    // `range_reference_t<const V>` does not model input range
+    static_assert(!JoinWithViewHasConstBegin<
+                  BasicView<std::vector<InputRangeButOutputWhenConst<int>>, ViewProperties{}, forward_iterator>,
+                  BasicView<Pat, ViewProperties{}, forward_iterator>>);
+  }
+
+  { // Check situation when iterators returned by `begin()` and `begin() const` are the same
+    using JWV = std::ranges::join_with_view<MaybeSimpleForwardView<true>, MaybeSimplePattern<true>>;
+    static_assert(std::same_as<std::ranges::iterator_t<JWV&>, std::ranges::iterator_t<const JWV&>>);
+  }
+}
+
+constexpr bool test() {
+  test_begin();
+  test_const_begin();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/constraints.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/constraints.compile.pass.cpp
new file mode 100644
index 00000000000000..5d446d9a0a60ed
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/constraints.compile.pass.cpp
@@ -0,0 +1,266 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <ranges>
+
+// template <input_range V, forward_range Pattern>
+//  requires view<V> && input_range<range_reference_t<V>> && view<Pattern> &&
+//           compatible-joinable-ranges<range_reference_t<V>, Pattern>
+// class join_with_view;
+
+#include <ranges>
+
+#include <cstddef>
+#include <vector>
+
+#include "test_iterators.h"
+
+template <class View, class Pattern>
+concept CanFormJoinWithView = requires { typename std::ranges::join_with_view<View, Pattern>; };
+
+// join_with_view is not valid when `V` is not an input_range
+namespace test_when_view_is_not_an_input_range {
+struct View : std::ranges::view_base {
+  using It = cpp20_output_iterator<std::vector<int>*>;
+  It begin();
+  sentinel_wrapper<It> end();
+};
+
+struct Pattern : std::ranges::view_base {
+  int* begin();
+  int* end();
+};
+
+static_assert(std::ranges::range<View>);
+static_assert(!std::ranges::input_range<View>);
+static_assert(std::ranges::view<View>);
+static_assert(std::ranges::forward_range<Pattern>);
+static_assert(std::ranges::view<Pattern>);
+static_assert(!CanFormJoinWithView<View, Pattern>);
+} // namespace test_when_view_is_not_an_input_range
+
+// join_with_view is not valid when `Pattern` is not a forward_range
+namespace test_when_pattern_is_not_a_forward_range {
+struct View : std::ranges::view_base {
+  std::vector<float>* begin();
+  std::vector<float>* end();
+};
+
+struct Pattern : std::ranges::view_base {
+  using It = cpp20_input_iterator<float*>;
+  It begin();
+  sentinel_wrapper<It> end();
+};
+
+static_assert(std::ranges::input_range<View>);
+static_assert(std::ranges::view<View>);
+static_assert(!std::ranges::forward_range<Pattern>);
+static_assert(std::ranges::view<Pattern>);
+static_assert(!CanFormJoinWithView<View, Pattern>);
+} // namespace test_when_pattern_is_not_a_forward_range
+
+// join_with_view is not valid when `V` does not model std::ranges::view
+namespace test_when_view_does_not_model_view {
+struct View {
+  std::vector<double>* begin();
+  std::vector<double>* end();
+};
+
+struct Pattern : std::ranges::view_base {
+  double* begin();
+  double* end();
+};
+
+static_assert(std::ranges::input_range<View>);
+static_assert(!std::ranges::view<View>);
+static_assert(std::ranges::forward_range<Pattern>);
+static_assert(std::ranges::view<Pattern>);
+static_assert(!CanFormJoinWithView<View, Pattern>);
+} // namespace test_when_view_does_not_model_view
+
+// join_with_view is not valid when `range_reference_t` of `V` is not an input_range
+namespace test_when_range_reference_t_of_view_is_not_an_input_range {
+struct InnerRange {
+  using It = cpp20_output_iterator<long*>;
+  It begin();
+  sentinel_wrapper<It> end();
+};
+
+struct View : std::ranges::view_base {
+  InnerRange* begin();
+  InnerRange* end();
+};
+
+struct Pattern : std::ranges::view_base {
+  long* begin();
+  long* end();
+};
+
+static_assert(std::ranges::range<InnerRange>);
+static_assert(!std::ranges::input_range<InnerRange>);
+static_assert(std::ranges::input_range<View>);
+static_assert(std::ranges::view<View>);
+static_assert(std::ranges::forward_range<Pattern>);
+static_assert(std::ranges::view<Pattern>);
+static_assert(!CanFormJoinWithView<View, Pattern>);
+} // namespace test_when_range_reference_t_of_view_is_not_an_input_range
+
+// join_with_view is not valid when `Pattern` does not model std::ranges::view
+namespace test_when_pattern_does_not_model_view {
+struct View : std::ranges::view_base {
+  std::vector<short>* begin();
+  std::vector<short>* end();
+};
+
+struct Pattern {
+  short* begin();
+  short* end();
+};
+
+static_assert(std::ranges::input_range<View>);
+static_assert(std::ranges::view<View>);
+static_assert(std::ranges::forward_range<Pattern>);
+static_assert(!std::ranges::view<Pattern>);
+static_assert(!CanFormJoinWithView<View, Pattern>);
+} // namespace test_when_pattern_does_not_model_view
+
+// join_with_view is not valid when `range_reference_t<View>` and pattern
+// does not model together compatible-joinable-ranges
+namespace test_when_used_ranges_are_not_compatible_joinable_ranges {
+using std::ranges::range_reference_t;
+using std::ranges::range_rvalue_reference_t;
+using std::ranges::range_value_t;
+
+template <class InnerRange>
+struct View : std::ranges::view_base {
+  InnerRange* begin();
+  InnerRange* end();
+};
+
+namespace no_common_range_value_type {
+struct InnerRange {
+  struct It {
+    using difference_type = ptrdiff_t;
+    struct value_type {};
+
+    struct reference {
+      operator value_type();
+      operator float();
+    };
+
+    It& operator++();
+    void operator++(int);
+    reference operator*() const;
+  };
+
+  It begin();
+  sentinel_wrapper<It> end();
+};
+
+struct Pattern : std::ranges::view_base {
+  const float* begin();
+  const float* end();
+};
+
+static_assert(std::ranges::input_range<InnerRange>);
+static_assert(std::ranges::forward_range<Pattern>);
+static_assert(std::ranges::view<Pattern>);
+static_assert(!std::common_with<range_value_t<InnerRange>, range_value_t<Pattern>>);
+static_assert(std::common_reference_with<range_reference_t<InnerRange>, range_reference_t<Pattern>>);
+static_assert(std::common_reference_with<range_rvalue_reference_t<InnerRange>, range_rvalue_reference_t<Pattern>>);
+static_assert(!CanFormJoinWithView<View<InnerRange>, Pattern>);
+} // namespace no_common_range_value_type
+
+namespace no_common_range_reference_type {
+struct ValueType {};
+
+struct InnerRange {
+  struct It {
+    using difference_type = ptrdiff_t;
+    using value_type      = ValueType;
+    struct reference {
+      operator value_type();
+    };
+
+    It& operator++();
+    void operator++(int);
+    reference operator*() const;
+  };
+
+  It begin();
+  sentinel_wrapper<It> end();
+};
+
+struct Pattern : std::ranges::view_base {
+  struct It {
+    using difference_type = ptrdiff_t;
+    using value_type      = ValueType;
+    struct reference {
+      operator value_type();
+    };
+
+    It& operator++();
+    It operator++(int);
+    reference operator*() const;
+    bool operator==(const It&) const;
+    friend value_type&& iter_move(const It&);
+  };
+
+  It begin();
+  It end();
+};
+
+static_assert(std::ranges::input_range<InnerRange>);
+static_assert(std::ranges::forward_range<Pattern>);
+static_assert(std::ranges::view<Pattern>);
+static_assert(std::common_with<range_value_t<InnerRange>, range_value_t<Pattern>>);
+static_assert(!std::common_reference_with<range_reference_t<InnerRange>, range_reference_t<Pattern>>);
+static_assert(std::common_reference_with<range_rvalue_reference_t<InnerRange>, range_rvalue_reference_t<Pattern>>);
+static_assert(!CanFormJoinWithView<View<InnerRange>, Pattern>);
+} // namespace no_common_range_reference_type
+
+namespace no_common_range_rvalue_reference_type {
+struct InnerRange {
+  using It = cpp20_input_iterator<int*>;
+  It begin();
+  sentinel_wrapper<It> end();
+};
+
+struct Pattern : std::ranges::view_base {
+  struct It {
+    using difference_type = ptrdiff_t;
+    struct value_type {
+      operator int() const;
+    };
+
+    struct rvalue_reference {
+      operator value_type();
+    };
+
+    It& operator++();
+    It operator++(int);
+    value_type& operator*() const;
+    bool operator==(const It&) const;
+    friend rvalue_reference iter_move(const It&);
+  };
+
+  It begin();
+  It end();
+};
+
+static_assert(std::ranges::input_range<InnerRange>);
+static_assert(std::ranges::forward_range<Pattern>);
+static_assert(std::ranges::view<Pattern>);
+static_assert(std::common_with<range_value_t<InnerRange>, range_value_t<Pattern>>);
+static_assert(std::common_reference_with<range_reference_t<InnerRange>, range_reference_t<Pattern>>);
+static_assert(!std::common_reference_with<range_rvalue_reference_t<InnerRange>, range_rvalue_reference_t<Pattern>>);
+static_assert(!CanFormJoinWithView<View<InnerRange>, Pattern>);
+} // namespace no_common_range_rvalue_reference_type
+} // namespace test_when_used_ranges_are_not_compatible_joinable_ranges
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/ctad.compile.pass.cpp
new file mode 100644
index 00000000000000..29d17dd08125f1
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/ctad.compile.pass.cpp
@@ -0,0 +1,230 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <ranges>
+
+// template<class R, class P>
+//   join_with_view(R&&, P&&) -> join_with_view<views::all_t<R>, views::all_t<P>>;
+//
+// template<input_range R>
+//   join_with_view(R&&, range_value_t<range_reference_t<R>>)
+//     -> join_with_view<views::all_t<R>, single_view<range_value_t<range_reference_t<R>>>>;
+
+#include <ranges>
+
+#include <deque>
+#include <type_traits>
+
+#include "test_iterators.h"
+
+struct View : std::ranges::view_base {
+  using It = cpp20_input_iterator<std::deque<int>*>;
+
+  View() = default;
+  It begin() const;
+  sentinel_wrapper<It> end() const;
+};
+
+static_assert(std::ranges::input_range<View>);
+static_assert(std::ranges::view<View>);
+
+struct Pattern : std::ranges::view_base {
+  Pattern() = default;
+  forward_iterator<int*> begin();
+  forward_iterator<int*> end();
+};
+
+static_assert(std::ranges::forward_range<Pattern>);
+static_assert(std::ranges::view<Pattern>);
+
+// A range that is not a view
+struct Range {
+  using It = cpp20_input_iterator<std::deque<int>*>;
+
+  Range() = default;
+  It begin() const;
+  sentinel_wrapper<It> end() const;
+};
+
+static_assert(std::ranges::input_range<Range>);
+static_assert(!std::ranges::view<Range>);
+
+// A pattern that is not a view
+struct RangePattern {
+  RangePattern() = default;
+  forward_iterator<int*> begin();
+  forward_iterator<int*> end();
+};
+
+static_assert(std::ranges::forward_range<RangePattern>);
+static_assert(!std::ranges::view<RangePattern>);
+
+void test_range_and_pattern_deduction_guide() {
+  { // Both `v` and `pat` model `std::ranges::view`.
+    {
+      View v;
+      Pattern pat;
+      std::ranges::join_with_view view(v, pat);
+      static_assert(std::is_same_v<decltype(view), std::ranges::join_with_view<View, Pattern>>);
+    }
+    {
+      View v;
+      std::ranges::join_with_view view(v, Pattern{});
+      static_assert(std::is_same_v<decltype(view), std::ranges::join_with_view<View, Pattern>>);
+    }
+    {
+      Pattern pat;
+      std::ranges::join_with_view view(View{}, pat);
+      static_assert(std::is_same_v<decltype(view), std::ranges::join_with_view<View, Pattern>>);
+    }
+    {
+      std::ranges::join_with_view view(View{}, Pattern{});
+      static_assert(std::is_same_v<decltype(view), std::ranges::join_with_view<View, Pattern>>);
+    }
+  }
+
+  { // Only `pat` models `std::ranges::view`.
+    {
+      Range v;
+      Pattern pat;
+      std::ranges::join_with_view view(v, pat);
+      static_assert(std::is_same_v<decltype(view), std::ranges::join_with_view<std::ranges::ref_view<Range>, Pattern>>);
+    }
+    {
+      Range v;
+      std::ranges::join_with_view view(v, Pattern{});
+      static_assert(std::is_same_v<decltype(view), std::ranges::join_with_view<std::ranges::ref_view<Range>, Pattern>>);
+    }
+    {
+      Pattern pat;
+      std::ranges::join_with_view view(Range{}, pat);
+      static_assert(
+          std::is_same_v<decltype(view), std::ranges::join_with_view<std::ranges::owning_view<Range>, Pattern>>);
+    }
+    {
+      std::ranges::join_with_view view(Range{}, Pattern{});
+      static_assert(
+          std::is_same_v<decltype(view), std::ranges::join_with_view<std::ranges::owning_view<Range>, Pattern>>);
+    }
+  }
+
+  { // Only `v` models `std::ranges::view`.
+    {
+      View v;
+      RangePattern pat;
+      std::ranges::join_with_view view(v, pat);
+      static_assert(
+          std::is_same_v<decltype(view), std::ranges::join_with_view<View, std::ranges::ref_view<RangePattern>>>);
+    }
+    {
+      View v;
+      std::ranges::join_with_view view(v, RangePattern{});
+      static_assert(
+          std::is_same_v<decltype(view), std::ranges::join_with_view<View, std::ranges::owning_view<RangePattern>>>);
+    }
+    {
+      RangePattern pat;
+      std::ranges::join_with_view view(View{}, pat);
+      static_assert(
+          std::is_same_v<decltype(view), std::ranges::join_with_view<View, std::ranges::ref_view<RangePattern>>>);
+    }
+    {
+      std::ranges::join_with_view view(View{}, RangePattern{});
+      static_assert(
+          std::is_same_v<decltype(view), std::ranges::join_with_view<View, std::ranges::owning_view<RangePattern>>>);
+    }
+  }
+
+  { // Both `v` and `pat` don't model `std::ranges::view`.
+    {
+      Range r;
+      RangePattern pat;
+      std::ranges::join_with_view view(r, pat);
+      static_assert(std::is_same_v<
+                    decltype(view),
+                    std::ranges::join_with_view<std::ranges::ref_view<Range>, std::ranges::ref_view<RangePattern>>>);
+    }
+    {
+      Range r;
+      std::ranges::join_with_view view(r, RangePattern{});
+      static_assert(std::is_same_v<
+                    decltype(view),
+                    std::ranges::join_with_view<std::ranges::ref_view<Range>, std::ranges::owning_view<RangePattern>>>);
+    }
+    {
+      RangePattern pat;
+      std::ranges::join_with_view view(Range{}, pat);
+      static_assert(std::is_same_v<
+                    decltype(view),
+                    std::ranges::join_with_view<std::ranges::owning_view<Range>, std::ranges::ref_view<RangePattern>>>);
+    }
+    {
+      std::ranges::join_with_view view(Range{}, RangePattern{});
+      static_assert(
+          std::is_same_v<
+              decltype(view),
+              std::ranges::join_with_view<std::ranges::owning_view<Range>, std::ranges::owning_view<RangePattern>>>);
+    }
+  }
+}
+
+void test_range_and_element_deduction_guide() {
+  { // Element is lvalue
+    int elem = 0;
+
+    {
+      View v;
+      std::ranges::join_with_view view(v, elem);
+      static_assert(std::is_same_v<decltype(view), std::ranges::join_with_view<View, std::ranges::single_view<int>>>);
+    }
+    {
+      std::ranges::join_with_view view(View{}, elem);
+      static_assert(std::is_same_v<decltype(view), std::ranges::join_with_view<View, std::ranges::single_view<int>>>);
+    }
+    {
+      Range r;
+      std::ranges::join_with_view view(r, elem);
+      static_assert(
+          std::is_same_v<decltype(view),
+                         std::ranges::join_with_view<std::ranges::ref_view<Range>, std::ranges::single_view<int>>>);
+    }
+    {
+      std::ranges::join_with_view view(Range{}, elem);
+      static_assert(
+          std::is_same_v<decltype(view),
+                         std::ranges::join_with_view<std::ranges::owning_view<Range>, std::ranges::single_view<int>>>);
+    }
+  }
+
+  { // Element is rvalue
+    {
+      View v;
+      std::ranges::join_with_view view(v, 1);
+      static_assert(std::is_same_v<decltype(view), std::ranges::join_with_view<View, std::ranges::single_view<int>>>);
+    }
+    {
+      std::ranges::join_with_view view(View{}, 1);
+      static_assert(std::is_same_v<decltype(view), std::ranges::join_with_view<View, std::ranges::single_view<int>>>);
+    }
+    {
+      Range r;
+      std::ranges::join_with_view view(r, 1);
+      static_assert(
+          std::is_same_v<decltype(view),
+                         std::ranges::join_with_view<std::ranges::ref_view<Range>, std::ranges::single_view<int>>>);
+    }
+    {
+      std::ranges::join_with_view view(Range{}, 1);
+      static_assert(
+          std::is_same_v<decltype(view),
+                         std::ranges::join_with_view<std::ranges::owning_view<Range>, std::ranges::single_view<int>>>);
+    }
+  }
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/ctor.default.pass.cpp
new file mode 100644
index 00000000000000..2a2238677ecbb8
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/ctor.default.pass.cpp
@@ -0,0 +1,73 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <ranges>
+
+// join_with_view()
+//   requires default_initializable<V> && default_initializable<Pattern> = default;
+
+#include <ranges>
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <type_traits>
+
+static constexpr auto view = std::to_array<std::array<int, 2>>({{1, 2}, {3, 4}, {5, 6}});
+
+struct TrivialView : std::ranges::view_base {
+  int val_; // intentionally uninitialized
+
+  constexpr auto begin() { return view.data(); }
+  constexpr auto end() { return view.data() + view.size(); }
+};
+
+static_assert(std::is_trivial_v<TrivialView>);
+
+struct NonDefaultConstructibleView : TrivialView {
+  NonDefaultConstructibleView(int);
+};
+
+struct TrivialPattern : std::ranges::view_base {
+  int val_; // intentionally uninitialized
+
+  constexpr int* begin() { return &val_; }
+  constexpr int* end() { return &val_ + 1; }
+};
+
+static_assert(std::is_trivial_v<TrivialPattern>);
+
+struct NonDefaultConstructiblePattern : TrivialPattern {
+  NonDefaultConstructiblePattern(int);
+};
+
+constexpr bool test() {
+  // Check if `base_` and `pattern_` are value initialised
+  {
+    std::ranges::join_with_view<TrivialView, TrivialPattern> v;
+    assert(std::move(v).base().val_ == 0);
+    assert(std::ranges::equal(v, std::array{1, 2, 0, 3, 4, 0, 5, 6}));
+  }
+
+  static_assert(std::default_initializable<std::ranges::join_with_view<TrivialView, TrivialPattern>>);
+  static_assert(!std::default_initializable<std::ranges::join_with_view<TrivialView, NonDefaultConstructiblePattern>>);
+  static_assert(!std::default_initializable<std::ranges::join_with_view<NonDefaultConstructibleView, TrivialPattern>>);
+  static_assert(!std::default_initializable<
+                std::ranges::join_with_view<NonDefaultConstructibleView, NonDefaultConstructiblePattern>>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/ctor.range.element.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/ctor.range.element.pass.cpp
new file mode 100644
index 00000000000000..65e1b2b14e2b06
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/ctor.range.element.pass.cpp
@@ -0,0 +1,244 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <ranges>
+
+// template<input_range R>
+//   requires constructible_from<V, views::all_t<R>> &&
+//   constructible_from<Pattern, single_view<range_value_t<InnerRng>>>
+// constexpr explicit join_with_view(R&& r, range_value_t<InnerRng> e);
+
+#include <ranges>
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <type_traits>
+#include <utility>
+
+#include "../types.h"
+#include "test_iterators.h"
+#include "test_range.h"
+
+struct MoveOnlyInt {
+  MoveOnlyInt()                         = default;
+  MoveOnlyInt(MoveOnlyInt&&)            = default;
+  MoveOnlyInt& operator=(MoveOnlyInt&&) = default;
+
+  constexpr MoveOnlyInt(int val) : val_(val) {}
+  constexpr operator int() const { return val_; }
+
+  int val_ = 0;
+};
+
+template <>
+struct std::common_type<MoveOnlyInt, int> {
+  using type = int;
+};
+
+template <>
+struct std::common_type<int, MoveOnlyInt> {
+  using type = int;
+};
+
+struct OutputView : std::ranges::view_base {
+  using It = cpp20_output_iterator<int*>;
+  It begin() const;
+  sentinel_wrapper<It> end() const;
+};
+
+static_assert(std::ranges::output_range<OutputView, int>);
+static_assert(std::ranges::view<OutputView>);
+
+struct InputRange {
+  using It = cpp20_input_iterator<int*>;
+  It begin() const;
+  sentinel_wrapper<It> end() const;
+};
+
+struct InputView : InputRange, std::ranges::view_base {};
+
+static_assert(std::ranges::input_range<InputRange>);
+static_assert(std::ranges::input_range<const InputRange>);
+static_assert(std::ranges::view<InputView>);
+static_assert(std::ranges::input_range<InputView>);
+static_assert(std::ranges::input_range<const InputView>);
+
+class View : public std::ranges::view_base {
+  using OuterRange = std::array<std::array<MoveOnlyInt, 2>, 3>;
+
+  static constexpr OuterRange range_on_input_view            = {{{1, 1}, {1, 1}, {1, 1}}};
+  static constexpr OuterRange range_on_ref_input_range       = {{{2, 2}, {2, 2}, {2, 2}}};
+  static constexpr OuterRange range_on_const_ref_input_range = {{{3, 3}, {3, 3}, {3, 3}}};
+  static constexpr OuterRange range_on_owning_input_range    = {{{4, 4}, {4, 4}, {4, 4}}};
+
+  const OuterRange* r_;
+
+public:
+  // Those functions should never be called in this test.
+  View(View&&) { assert(false); }
+  View(OutputView) { assert(false); }
+  View& operator=(View&&) {
+    assert(false);
+    return *this;
+  }
+
+  constexpr explicit View(InputView) : r_(&range_on_input_view) {}
+  constexpr explicit View(InputRange) = delete;
+  constexpr explicit View(std::ranges::ref_view<InputRange>) : r_(&range_on_ref_input_range) {}
+  constexpr explicit View(std::ranges::ref_view<const InputRange>) : r_(&range_on_const_ref_input_range) {}
+  constexpr explicit View(std::ranges::owning_view<InputRange>) : r_(&range_on_owning_input_range) {}
+
+  constexpr auto begin() const { return r_->begin(); }
+  constexpr auto end() const { return r_->end(); }
+};
+
+static_assert(std::ranges::input_range<View>);
+static_assert(std::ranges::input_range<const View>);
+
+class Pattern : public std::ranges::view_base {
+  int val_;
+
+public:
+  // Those functions should never be called in this test.
+  Pattern(Pattern&&) { assert(false); }
+  template <class T>
+  Pattern(const std::ranges::single_view<T>&) {
+    assert(false);
+  }
+  Pattern& operator=(Pattern&&) {
+    assert(false);
+    return *this;
+  }
+
+  template <class T>
+  constexpr explicit Pattern(std::ranges::single_view<T>&& v) : val_(v[0]) {}
+
+  constexpr const int* begin() const { return &val_; }
+  constexpr const int* end() const { return &val_ + 1; }
+};
+
+static_assert(std::ranges::forward_range<Pattern>);
+static_assert(std::ranges::forward_range<const Pattern>);
+
+constexpr void test_ctor_with_view_and_element() {
+  // Check construction from `r` and `e`, when `r` models `std::ranges::view`
+
+  { // `r` and `e` are glvalues
+    InputView r;
+    int e = 0;
+    std::ranges::join_with_view<View, Pattern> jwv(r, e);
+    assert(std::ranges::equal(jwv, std::array{1, 1, 0, 1, 1, 0, 1, 1}));
+  }
+
+  { // `r` and `e` are const glvalues
+    const InputView r;
+    const int e = 1;
+    std::ranges::join_with_view<View, Pattern> jwv(r, e);
+    assert(std::ranges::equal(jwv, std::array{1, 1, 1, 1, 1, 1, 1, 1}));
+  }
+
+  { // `r` and `e` are prvalues
+    std::ranges::join_with_view<View, Pattern> jwv(InputView{}, MoveOnlyInt{2});
+    assert(std::ranges::equal(jwv, std::array{1, 1, 2, 1, 1, 2, 1, 1}));
+  }
+
+  { // `r` and `e` are xvalues
+    InputView r;
+    MoveOnlyInt e = 3;
+    std::ranges::join_with_view<View, Pattern> jwv(std::move(r), std::move(e));
+    assert(std::ranges::equal(jwv, std::array{1, 1, 3, 1, 1, 3, 1, 1}));
+  }
+
+  // Check explicitness
+  static_assert(ConstructionIsExplicit<std::ranges::join_with_view<View, Pattern>, InputView, MoveOnlyInt>);
+  static_assert(ConstructionIsExplicit<std::ranges::join_with_view<View, Pattern>, InputView, int>);
+  static_assert(ConstructionIsExplicit<std::ranges::join_with_view<View, Pattern>, InputView&, int&>);
+  static_assert(ConstructionIsExplicit<std::ranges::join_with_view<View, Pattern>, const InputView, const int>);
+  static_assert(ConstructionIsExplicit<std::ranges::join_with_view<View, Pattern>, const InputView&, const int&>);
+}
+
+constexpr void test_ctor_with_non_view_and_element() {
+  // Check construction from `r` and `e`, when `r` does not model `std::ranges::view`
+
+  { // `r` and `e` are glvalues
+    InputRange r;
+    int e = 0;
+    std::ranges::join_with_view<View, Pattern> jwv(r, e);
+    assert(std::ranges::equal(jwv, std::array{2, 2, 0, 2, 2, 0, 2, 2}));
+  }
+
+  { // `r` and `e` are const glvalues
+    const InputRange r;
+    const int e = 1;
+    std::ranges::join_with_view<View, Pattern> jwv(r, e);
+    assert(std::ranges::equal(jwv, std::array{3, 3, 1, 3, 3, 1, 3, 3}));
+  }
+
+  { // `r` and `e` are prvalues
+    std::ranges::join_with_view<View, Pattern> jwv(InputRange{}, MoveOnlyInt{2});
+    assert(std::ranges::equal(jwv, std::array{4, 4, 2, 4, 4, 2, 4, 4}));
+  }
+
+  { // `r` and `e` are xvalues
+    InputRange r;
+    MoveOnlyInt e = 3;
+    std::ranges::join_with_view<View, Pattern> jwv(std::move(r), std::move(e));
+    assert(std::ranges::equal(jwv, std::array{4, 4, 3, 4, 4, 3, 4, 4}));
+  }
+
+  // Check explicitness
+  static_assert(ConstructionIsExplicit<std::ranges::join_with_view<View, Pattern>, InputRange, MoveOnlyInt>);
+  static_assert(ConstructionIsExplicit<std::ranges::join_with_view<View, Pattern>, InputRange, int>);
+  static_assert(ConstructionIsExplicit<std::ranges::join_with_view<View, Pattern>, InputRange&, int&>);
+  static_assert(ConstructionIsExplicit<std::ranges::join_with_view<View, Pattern>, const InputRange&, const int&>);
+}
+
+constexpr void test_constraints() {
+  { // `R` is not an input range
+    using R = OutputView;
+    static_assert(!std::ranges::input_range<R>);
+    static_assert(std::constructible_from<View, std::views::all_t<R>>);
+    static_assert(std::constructible_from<Pattern, std::ranges::single_view<int>>);
+    static_assert(!std::constructible_from<std::ranges::join_with_view<View, Pattern>, R, int>);
+  }
+
+  { // `V` is not constructible from `views::all_t<R>`
+    using R = test_range<cpp20_input_iterator>;
+    static_assert(std::ranges::input_range<R>);
+    static_assert(!std::constructible_from<View, std::views::all_t<R>>);
+    static_assert(std::constructible_from<Pattern, std::ranges::single_view<int>>);
+    static_assert(!std::constructible_from<std::ranges::join_with_view<View, Pattern>, R, int>);
+  }
+
+  { // `Pattern` is not constructible from `single_view<range_value_t<InnerRng>>`
+    using R   = InputView;
+    using Pat = test_view<forward_iterator>;
+    static_assert(std::ranges::input_range<R>);
+    static_assert(std::constructible_from<View, std::views::all_t<R>>);
+    static_assert(!std::constructible_from<Pat, std::ranges::single_view<int>>);
+    static_assert(!std::constructible_from<std::ranges::join_with_view<View, Pat>, R, int>);
+  }
+}
+
+constexpr bool test() {
+  test_ctor_with_view_and_element();
+  test_ctor_with_non_view_and_element();
+  test_constraints();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/ctor.range.pattern.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/ctor.range.pattern.pass.cpp
new file mode 100644
index 00000000000000..2fa11a504ca02f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/ctor.range.pattern.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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <ranges>
+
+// constexpr explicit join_with_view(V base, Pattern pattern);
+
+#include <ranges>
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <utility>
+
+#include "../types.h"
+
+class View : public std::ranges::view_base {
+  using OuterRange = std::array<std::array<int, 2>, 3>;
+
+  static constexpr OuterRange default_range = {{{1, 2}, {3, 4}, {5, 6}}};
+  static constexpr OuterRange range_on_move = {{{6, 5}, {4, 3}, {2, 1}}};
+
+  const OuterRange* r_ = &default_range;
+
+public:
+  View() = default;
+  constexpr View(const View&) : r_(&default_range) {}
+  constexpr View(View&&) : r_(&range_on_move) {}
+
+  constexpr View& operator=(View) {
+    r_ = &default_range;
+    return *this;
+  }
+
+  constexpr auto begin() { return r_->begin(); }
+  constexpr auto end() { return r_->end(); }
+};
+
+class Pattern : public std::ranges::view_base {
+  using PatternRange = std::array<int, 2>;
+
+  static constexpr PatternRange default_range = {0, 0};
+  static constexpr PatternRange range_on_move = {7, 7};
+
+  const PatternRange* val_ = &default_range;
+
+public:
+  Pattern() = default;
+  constexpr Pattern(const Pattern&) : val_(&default_range) {}
+  constexpr Pattern(Pattern&&) : val_(&range_on_move) {}
+
+  constexpr Pattern& operator=(Pattern) {
+    val_ = &default_range;
+    return *this;
+  }
+
+  constexpr auto begin() { return val_->begin(); }
+  constexpr auto end() { return val_->end(); }
+};
+
+constexpr bool test() {
+  {   // Check construction from `view` and `pattern`
+    { // `view` and `pattern` are glvalues
+      View v;
+      Pattern p;
+      std::ranges::join_with_view<View, Pattern> jwv(v, p);
+      assert(std::ranges::equal(jwv, std::array{6, 5, 7, 7, 4, 3, 7, 7, 2, 1}));
+    }
+
+    { // `view` and `pattern` are const glvalues
+      const View v;
+      const Pattern p;
+      std::ranges::join_with_view<View, Pattern> jwv(v, p);
+      assert(std::ranges::equal(jwv, std::array{6, 5, 7, 7, 4, 3, 7, 7, 2, 1}));
+    }
+
+    { // `view` and `pattern` are prvalues
+      std::ranges::join_with_view<View, Pattern> jwv(View{}, Pattern{});
+      assert(std::ranges::equal(jwv, std::array{6, 5, 7, 7, 4, 3, 7, 7, 2, 1}));
+    }
+
+    { // `view` and `pattern` are xvalues
+      View v;
+      Pattern p;
+      std::ranges::join_with_view<View, Pattern> jwv(std::move(v), std::move(p));
+      assert(std::ranges::equal(jwv, std::array{6, 5, 7, 7, 4, 3, 7, 7, 2, 1}));
+    }
+  }
+
+  // Check explicitness
+  static_assert(ConstructionIsExplicit<std::ranges::join_with_view<View, Pattern>, View, Pattern>);
+  static_assert(ConstructionIsExplicit<std::ranges::join_with_view<View, Pattern>, View&, Pattern&>);
+  static_assert(ConstructionIsExplicit<std::ranges::join_with_view<View, Pattern>, const View, const Pattern>);
+  static_assert(ConstructionIsExplicit<std::ranges::join_with_view<View, Pattern>, const View&, const Pattern&>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/end.pass.cpp
new file mode 100644
index 00000000000000..c6c54f74f6b77f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/end.pass.cpp
@@ -0,0 +1,244 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <ranges>
+
+// constexpr auto end() {
+//   if constexpr (forward_range<V> &&
+//                 is_reference_v<InnerRng> && forward_range<InnerRng> &&
+//                 common_range<V> && common_range<InnerRng>)
+//     return iterator<simple-view<V> && simple-view<Pattern>>{*this, ranges::end(base_)};
+//   else
+//     return sentinel<simple-view<V> && simple-view<Pattern>>{*this};
+// }
+// constexpr auto end() const
+//   requires forward_range<const V> && forward_range<const Pattern> &&
+//            is_reference_v<range_reference_t<const V>> &&
+//            input_range<range_reference_t<const V>> {
+//   using InnerConstRng = range_reference_t<const V>;
+//   if constexpr (forward_range<InnerConstRng> &&
+//                 common_range<const V> && common_range<InnerConstRng>)
+//     return iterator<true>{*this, ranges::end(base_)};
+//   else
+//     return sentinel<true>{*this};
+// }
+
+// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=10000000
+
+#include <ranges>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "../types.h"
+#include "test_iterators.h"
+
+template <class V, class Pattern>
+concept JoinWithViewHasConstEnd = requires(const std::ranges::join_with_view<V, Pattern> jwv) { jwv.end(); };
+
+template <size_t Bits>
+  requires(Bits < (1 << 7))
+constexpr void test_end() {
+  constexpr bool v_models_forward_range           = Bits & (1 << 0);
+  constexpr bool inner_range_is_reference         = Bits & (1 << 1);
+  constexpr bool inner_range_models_forward_range = Bits & (1 << 2);
+  constexpr bool v_models_common_range            = Bits & (1 << 3);
+  constexpr bool inner_range_models_common_range  = Bits & (1 << 4);
+  constexpr bool v_models_simple_range            = Bits & (1 << 5);
+  constexpr bool pattern_models_simple_range      = Bits & (1 << 6);
+
+  constexpr ViewProperties inner_range_props{.common = inner_range_models_common_range};
+  using InnerRange =
+      std::conditional_t<inner_range_models_forward_range,
+                         BasicView<std::vector<int>, inner_range_props, forward_iterator>,
+                         BasicView<std::vector<int>, inner_range_props, DefaultCtorInputIter>>;
+
+  constexpr ViewProperties v_props{.simple = v_models_simple_range, .common = v_models_common_range};
+  using UnderlyingV = std::conditional_t<inner_range_is_reference, std::vector<InnerRange>, RvalueVector<InnerRange>>;
+  using V           = std::conditional_t<v_models_forward_range,
+                                         BasicView<UnderlyingV, v_props, forward_iterator>,
+                                         BasicView<UnderlyingV, v_props, DefaultCtorInputIter>>;
+
+  using UnderlyingPattern = std::vector<int>;
+  using Pattern = BasicView<UnderlyingPattern, ViewProperties{.simple = pattern_models_simple_range}, forward_iterator>;
+
+  using JWV  = std::ranges::join_with_view<V, Pattern>;
+  using Iter = std::ranges::iterator_t<JWV>;
+
+  // Test when `JWV` models common range
+  static_assert(std::same_as<Iter, std::ranges::sentinel_t<JWV>> ==
+                (v_models_forward_range && inner_range_is_reference && inner_range_models_forward_range &&
+                 v_models_common_range && inner_range_models_common_range));
+
+  { // `V` and `Pattern` are empty
+    V v{};
+    Pattern pattern{};
+    JWV jwv(std::move(v), std::move(pattern));
+    Iter it                                   = jwv.begin();
+    std::sentinel_for<Iter> decltype(auto) se = jwv.end();
+    assert(it == se);
+  }
+
+  { // `V` is empty, `Pattern` contains some elements
+    V v{};
+    Pattern pattern{std::vector<int>{0}};
+    JWV jwv(std::move(v), std::move(pattern));
+    Iter it                                   = jwv.begin();
+    std::sentinel_for<Iter> decltype(auto) se = jwv.end();
+    assert(it == se);
+  }
+
+  { // `V` is not empty, `Pattern is empty`
+    V v{UnderlyingV{
+        std::vector<InnerRange>{InnerRange(std::vector<int>{1, 2, 3}), InnerRange(std::vector<int>{4, 5, 6})}}};
+    Pattern pattern{};
+    JWV jwv(std::move(v), std::move(pattern));
+    Iter it                                   = jwv.begin();
+    std::sentinel_for<Iter> decltype(auto) se = jwv.end();
+    assert(std::ranges::next(it, 6) == se);
+  }
+
+  { // `V` and `Pattern` are not empty
+    V v{UnderlyingV{std::vector<InnerRange>{
+        InnerRange(std::vector<int>{6, 5}),
+        InnerRange(std::vector<int>{4, 3}),
+        InnerRange(std::vector<int>{2, 1, 0})}}};
+    Pattern pattern{std::vector<int>{-1, -1}};
+    JWV jwv(std::move(v), std::move(pattern));
+    Iter it                                   = jwv.begin();
+    std::sentinel_for<Iter> decltype(auto) se = jwv.end();
+    assert(std::ranges::next(it, 11) == se);
+  }
+}
+
+constexpr void test_end() {}
+
+template <std::size_t Bits>
+  requires(Bits < (1 << 7))
+constexpr void test_const_end() {
+  constexpr bool const_v_models_forward_range           = Bits & (1 << 0);
+  constexpr bool const_pattern_models_forward_range     = Bits & (1 << 1);
+  constexpr bool inner_const_range_is_reference         = Bits & (1 << 2);
+  constexpr bool inner_const_range_models_input_range   = Bits & (1 << 3);
+  constexpr bool inner_const_range_models_forward_range = Bits & (1 << 4);
+  constexpr bool const_v_models_common_range            = Bits & (1 << 5);
+  constexpr bool inner_const_range_models_common_range  = Bits & (1 << 6);
+
+  constexpr ViewProperties inner_range_props{.common = inner_const_range_models_common_range};
+  using InnerRange =
+      std::conditional_t<inner_const_range_models_forward_range,
+                         BasicView<std::vector<int>, inner_range_props, forward_iterator>,
+                         std::conditional_t<inner_const_range_models_input_range,
+                                            BasicView<std::vector<int>, inner_range_props, DefaultCtorInputIter>,
+                                            InputRangeButOutputWhenConst<int>>>;
+
+  constexpr ViewProperties v_props{.common = const_v_models_common_range};
+  using UnderlyingV =
+      std::conditional_t<inner_const_range_is_reference, std::vector<InnerRange>, RvalueVector<InnerRange>>;
+  using V = std::conditional_t<const_v_models_forward_range,
+                               BasicView<UnderlyingV, v_props, forward_iterator>,
+                               BasicView<UnderlyingV, v_props, DefaultCtorInputIter>>;
+  using Pattern =
+      std::conditional_t<const_pattern_models_forward_range,
+                         BasicView<std::vector<int>, ViewProperties{}, forward_iterator>,
+                         ForwardViewButInputWhenConst<int>>;
+
+  using JWV = std::ranges::join_with_view<V, Pattern>;
+  static_assert(JoinWithViewHasConstEnd<V, Pattern> ==
+                (const_v_models_forward_range && const_pattern_models_forward_range && inner_const_range_is_reference &&
+                 (inner_const_range_models_input_range || inner_const_range_models_forward_range)));
+  static_assert(JoinWithViewHasConstEnd<V, Pattern> == std::ranges::range<const JWV>);
+
+  if constexpr (std::ranges::range<const JWV>) {
+    using ConstIter = std::ranges::iterator_t<const JWV>;
+
+    // Test when `const JWV` models common range
+    static_assert(std::same_as<ConstIter, std::ranges::sentinel_t<const JWV>> ==
+                  (inner_const_range_models_forward_range && const_v_models_common_range &&
+                   inner_const_range_models_common_range));
+
+    { // `const V` and `const Pattern` are empty
+      V v{};
+      Pattern pattern{};
+      const JWV jwv(std::move(v), std::move(pattern));
+      ConstIter it                                   = jwv.begin();
+      std::sentinel_for<ConstIter> decltype(auto) se = jwv.end();
+      assert(it == se);
+    }
+
+    { // `const V` is empty, `const Pattern` contains some elements
+      V v{};
+      Pattern pattern{std::vector<int>{1}};
+      const JWV jwv(std::move(v), std::move(pattern));
+      ConstIter it                                   = jwv.begin();
+      std::sentinel_for<ConstIter> decltype(auto) se = jwv.end();
+      assert(it == se);
+    }
+
+    { // `const V` is not empty, `const Pattern is empty`
+      V v{UnderlyingV{
+          std::vector<InnerRange>{InnerRange(std::vector<int>{1, 2, 3}), InnerRange(std::vector<int>{4, 5, 6})}}};
+      Pattern pattern{};
+      const JWV jwv(std::move(v), std::move(pattern));
+      ConstIter it                                   = jwv.begin();
+      std::sentinel_for<ConstIter> decltype(auto) se = jwv.end();
+      assert(std::ranges::next(it, 6) == se);
+    }
+
+    { // `const V` and `const Pattern` are not empty
+      V v{UnderlyingV{std::vector<InnerRange>{
+          InnerRange(std::vector<int>{1}), InnerRange(std::vector<int>{2, 2}), InnerRange(std::vector<int>{3, 3, 3})}}};
+      Pattern pattern{std::vector<int>{0}};
+      const JWV jwv(std::move(v), std::move(pattern));
+      ConstIter it                                   = jwv.begin();
+      std::sentinel_for<ConstIter> decltype(auto) se = jwv.end();
+      assert(std::ranges::next(it, 8) == se);
+    }
+  }
+}
+
+constexpr bool test() {
+  []<std::size_t... Bits>(std::index_sequence<Bits...>) {
+    (test_end<Bits>(), ...);
+    (test_const_end<Bits>(), ...);
+  }(std::make_index_sequence<(1 << 7)>{});
+
+  { // Check situation when iterators returned by `end()` and `end() const` are of the same type
+    using V             = BasicView<std::vector<std::string>, ViewProperties{.simple = true}, forward_iterator>;
+    using Pattern       = BasicView<std::string, ViewProperties{.simple = true}, forward_iterator>;
+    using JWV           = std::ranges::join_with_view<V, Pattern>;
+    using Sentinel      = std::ranges::sentinel_t<JWV&>;
+    using ConstSentinel = std::ranges::sentinel_t<const JWV&>;
+    static_assert(std::input_iterator<Sentinel>);
+    static_assert(std::input_iterator<ConstSentinel>);
+    static_assert(std::same_as<Sentinel, ConstSentinel>);
+  }
+
+  { // Check situation when sentinels returned by `end()` and `end() const` are of the same type
+    using V = BasicView<std::vector<std::string>, ViewProperties{.simple = true, .common = false}, forward_iterator>;
+    using Pattern       = BasicView<std::string, ViewProperties{.simple = true}, forward_iterator>;
+    using JWV           = std::ranges::join_with_view<V, Pattern>;
+    using Sentinel      = std::ranges::sentinel_t<JWV&>;
+    using ConstSentinel = std::ranges::sentinel_t<const JWV&>;
+    static_assert(!std::input_iterator<Sentinel>);
+    static_assert(!std::input_iterator<ConstSentinel>);
+    static_assert(std::same_as<Sentinel, ConstSentinel>);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/inheritance.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/inheritance.compile.pass.cpp
new file mode 100644
index 00000000000000..62786c39a8caf0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/inheritance.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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <ranges>
+
+// class join_with_view : public view_interface<join_with_view<V, Pattern>>
+
+#include <ranges>
+
+#include <concepts>
+#include <string>
+#include <vector>
+
+template <class T>
+struct View : std::ranges::view_base {
+  std::vector<T>* begin();
+  std::vector<T>* end();
+};
+
+template <class T>
+struct Pattern : std::ranges::view_base {
+  T* begin();
+  T* end();
+};
+
+template <class T>
+using JoinWithView = std::ranges::join_with_view<View<T>, Pattern<T>>;
+
+static_assert(std::derived_from<JoinWithView<int>, std::ranges::view_interface<JoinWithView<int>>>);
+static_assert(std::derived_from<JoinWithView<void*>, std::ranges::view_interface<JoinWithView<void*>>>);
+static_assert(std::derived_from<JoinWithView<std::string>, std::ranges::view_interface<JoinWithView<std::string>>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/types.h b/libcxx/test/std/ranges/range.adaptors/range.join.with/types.h
new file mode 100644
index 00000000000000..be551202966820
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/types.h
@@ -0,0 +1,267 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_JOIN_WITH_TYPES_H
+#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_JOIN_WITH_TYPES_H
+
+#include <initializer_list>
+#include <ranges>
+#include <string>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "test_range.h"
+
+template <class Tp>
+void pass_value(Tp);
+
+template <class Tp, class... Args>
+concept ConstructionIsExplicit = std::constructible_from<Tp, Args...> && !requires(Args&&... args) {
+  pass_value<Tp>({std::forward<Args>(args)...});
+};
+
+struct ViewProperties {
+  bool simple = false;
+  bool common = true;
+};
+
+template <std::ranges::input_range Data,
+          ViewProperties Prop,
+          template <class...>
+          class It,
+          template <class...> class ConstIt = It>
+class BasicView : public std::ranges::view_base {
+  Data data_;
+
+public:
+  constexpr BasicView()
+    requires std::default_initializable<Data>
+  = default;
+
+  template <class R>
+  constexpr explicit BasicView(R&& r)
+    requires requires { std::ranges::to<Data>(std::forward<R>(r)); }
+      /*******/ : data_(std::ranges::to<Data>(std::forward<R>(r))) {}
+
+  constexpr explicit BasicView(std::initializer_list<std::ranges::range_value_t<Data>> il)
+      : data_(std::ranges::to<Data>(il)) {}
+
+  constexpr auto begin()
+    requires(!Prop.simple)
+  {
+    return It(data_.begin());
+  }
+
+  constexpr auto end()
+    requires(!Prop.simple)
+  {
+    if constexpr (Prop.common)
+      return It(data_.end());
+    else
+      return sentinel_wrapper(It(data_.end()));
+  }
+
+  constexpr auto begin() const { return ConstIt(data_.begin()); }
+
+  constexpr auto end() const {
+    if constexpr (Prop.common)
+      return ConstIt(data_.end());
+    else
+      return sentinel_wrapper(ConstIt(data_.end()));
+  }
+};
+
+template <class Tp, ViewProperties Prop, template <class...> class It, template <class...> class ConstIt = It>
+using BasicVectorView = BasicView<std::vector<Tp>, Prop, It, ConstIt>;
+
+struct AsPrvalue {
+  template <class Tp>
+  constexpr auto operator()(Tp&& t) const {
+    return std::forward<Tp>(t);
+  }
+};
+
+template <class Tp>
+class RvalueVector {
+  using Vec = std::vector<Tp>;
+  std::ranges::transform_view<std::ranges::owning_view<Vec>, AsPrvalue> range_;
+
+public:
+  constexpr RvalueVector() = default;
+  constexpr explicit RvalueVector(Vec vec) : range_(std::move(vec), AsPrvalue{}) {}
+  constexpr explicit RvalueVector(std::initializer_list<Tp> il) : RvalueVector(Vec(il)) {}
+
+  constexpr auto begin() { return range_.begin(); }
+  constexpr auto end() { return range_.end(); }
+  constexpr auto begin() const { return range_.begin(); }
+  constexpr auto end() const { return range_.end(); }
+};
+
+template <class It>
+class DefaultCtorInputIter {
+  It it_ = It();
+
+public:
+  using value_type      = std::iter_value_t<It>;
+  using difference_type = std::iter_difference_t<It>;
+
+  DefaultCtorInputIter() = default;
+  constexpr explicit DefaultCtorInputIter(It it) : it_(it) {}
+
+  constexpr DefaultCtorInputIter& operator++() {
+    ++it_;
+    return *this;
+  }
+
+  constexpr void operator++(int) { ++*this; }
+  constexpr decltype(auto) operator*() const { return *it_; }
+  constexpr bool operator==(const DefaultCtorInputIter&) const = default;
+};
+
+template <class It>
+DefaultCtorInputIter(It) -> DefaultCtorInputIter<It>;
+
+template <class Tp>
+class InputRangeButOutputWhenConst {
+  using Vec = std::vector<Tp>;
+  std::ranges::ref_view<Vec> range_;
+
+public:
+  constexpr explicit InputRangeButOutputWhenConst(Vec& vec) : range_(vec) {}
+
+  constexpr auto begin() { return cpp20_input_iterator(range_.begin()); }
+  constexpr auto end() { return sentinel_wrapper(cpp20_input_iterator(range_.end())); }
+  constexpr auto begin() const { return cpp20_output_iterator(range_.begin()); }
+  constexpr auto end() const { return sentinel_wrapper(cpp20_output_iterator(range_.end())); }
+};
+
+template <class Tp>
+using ForwardViewButInputWhenConst =
+    BasicVectorView<Tp, ViewProperties{.common = false}, forward_iterator, cpp20_input_iterator>;
+
+template <class It>
+class ForwardIteratorWithInputCategory {
+  It it_ = It();
+
+public:
+  using value_type        = std::iter_value_t<It>;
+  using difference_type   = std::iter_difference_t<It>;
+  using iterator_concept  = std::forward_iterator_tag;
+  using iterator_category = std::input_iterator_tag;
+
+  ForwardIteratorWithInputCategory() = default;
+  explicit ForwardIteratorWithInputCategory(It it);
+
+  std::iter_reference_t<It> operator*() const;
+  ForwardIteratorWithInputCategory& operator++();
+  ForwardIteratorWithInputCategory operator++(int);
+  bool operator==(const ForwardIteratorWithInputCategory&) const;
+};
+
+template <class It>
+explicit ForwardIteratorWithInputCategory(It) -> ForwardIteratorWithInputCategory<It>;
+
+template <class It>
+class EqComparableInputIter {
+  It it_;
+
+public:
+  using value_type      = std::iter_value_t<It>;
+  using difference_type = std::iter_difference_t<It>;
+
+  constexpr explicit EqComparableInputIter(It it) : it_(it) {}
+
+  constexpr decltype(auto) operator*() const { return *it_; }
+  constexpr EqComparableInputIter& operator++() {
+    ++it_;
+    return *this;
+  }
+  constexpr void operator++(int) { ++it_; }
+
+  friend constexpr It base(const EqComparableInputIter& i) { return i.it_; }
+
+  friend constexpr bool operator==(const EqComparableInputIter& left, const EqComparableInputIter& right) {
+    return left.it_ == right.it_;
+  }
+};
+
+template <class It>
+EqComparableInputIter(It) -> EqComparableInputIter<It>;
+
+template <class Val>
+struct ConstOppositeView : std::ranges::view_base {
+  const Val* begin();
+  sentinel_wrapper<const Val*> end();
+  Val* begin() const;
+  sentinel_wrapper<Val*> end() const;
+};
+
+namespace selftest {
+using BV1 = BasicView<std::string, ViewProperties{.simple = true}, forward_iterator>;
+static_assert(std::ranges::forward_range<BV1>);
+static_assert(!std::ranges::bidirectional_range<BV1>);
+static_assert(std::ranges::common_range<BV1>);
+static_assert(simple_view<BV1>);
+
+using BV2 =
+    BasicView<RvalueVector<std::string>, ViewProperties{.simple = false, .common = false}, cpp20_input_iterator>;
+static_assert(std::ranges::input_range<BV2>);
+static_assert(!std::ranges::forward_range<BV2>);
+static_assert(!std::ranges::common_range<BV2>);
+static_assert(!std::is_reference_v<std::ranges::range_reference_t<BV2>>);
+static_assert(!simple_view<BV2>);
+
+using RV = RvalueVector<int>;
+static_assert(std::movable<RV>);
+static_assert(std::ranges::random_access_range<RV>);
+static_assert(std::ranges::random_access_range<const RV>);
+static_assert(!std::is_reference_v<std::ranges::range_reference_t<RV>>);
+static_assert(!std::is_reference_v<std::ranges::range_reference_t<const RV>>);
+
+using DCII = DefaultCtorInputIter<int*>;
+static_assert(std::default_initializable<DCII>);
+static_assert(std::sentinel_for<DCII, DCII>);
+static_assert(std::input_iterator<DCII>);
+static_assert(!std::forward_iterator<DCII>);
+
+using IRBOWC = InputRangeButOutputWhenConst<int>;
+static_assert(std::ranges::input_range<IRBOWC>);
+static_assert(std::ranges::output_range<const IRBOWC&, int>);
+
+using FVBIWC = ForwardViewButInputWhenConst<int>;
+static_assert(std::default_initializable<FVBIWC>);
+static_assert(std::ranges::view<FVBIWC>);
+static_assert(std::ranges::forward_range<FVBIWC>);
+static_assert(!std::ranges::common_range<FVBIWC>);
+static_assert(std::ranges::input_range<const FVBIWC&>);
+static_assert(!std::ranges::forward_range<const FVBIWC&>);
+static_assert(!std::ranges::common_range<const FVBIWC&>);
+
+using FIWIC = ForwardIteratorWithInputCategory<long*>;
+static_assert(std::forward_iterator<FIWIC>);
+static_assert(std::same_as<FIWIC::iterator_category, std::input_iterator_tag>);
+static_assert(std::same_as<FIWIC::iterator_category, std::iterator_traits<FIWIC>::iterator_category>);
+
+using ECII = EqComparableInputIter<int*>;
+static_assert(std::input_iterator<ECII>);
+static_assert(!std::forward_iterator<ECII>);
+static_assert(std::equality_comparable<ECII>);
+
+using COV = ConstOppositeView<int>;
+static_assert(std::ranges::view<COV>);
+static_assert(std::ranges::range<const COV>);
+static_assert(!std::ranges::common_range<COV>);
+static_assert(!std::ranges::common_range<const COV>);
+static_assert(std::convertible_to<std::ranges::iterator_t<const COV>, std::ranges::iterator_t<COV>>);
+static_assert(!std::convertible_to<std::ranges::iterator_t<COV>, std::ranges::iterator_t<const COV>>);
+} // namespace selftest
+
+#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_JOIN_WITH_TYPES_H
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index fe5bab05195a38..8c1aecb6fbd1a8 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1003,7 +1003,6 @@ def add_version_header(tc):
             "name": "__cpp_lib_ranges_join_with",
             "values": {"c++23": 202202},
             "headers": ["ranges"],
-            "unimplemented": True,
         },
         {
             "name": "__cpp_lib_ranges_repeat",

>From 23708578aa566f41af16256c8cbbded75d8dabe4 Mon Sep 17 00:00:00 2001
From: Jakub Mazurkiewicz <mazkuba3 at gmail.com>
Date: Thu, 21 Nov 2024 15:56:58 +0100
Subject: [PATCH 02/15] Add `[[nodiscard]]` to private functions

---
 libcxx/include/__ranges/join_with_view.h | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/libcxx/include/__ranges/join_with_view.h b/libcxx/include/__ranges/join_with_view.h
index bd80e0d937033c..d27f0fb168dc9e 100644
--- a/libcxx/include/__ranges/join_with_view.h
+++ b/libcxx/include/__ranges/join_with_view.h
@@ -232,28 +232,28 @@ struct join_with_view<_View, _Pattern>::__iterator
     }
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr _OuterIter& __get_outer() {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI constexpr _OuterIter& __get_outer() {
     if constexpr (forward_range<_Base>)
       return __outer_it_;
     else
       return *__parent_->__outer_it_;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr const _OuterIter& __get_outer() const {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI constexpr const _OuterIter& __get_outer() const {
     if constexpr (forward_range<_Base>)
       return __outer_it_;
     else
       return *__parent_->__outer_it_;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto& __update_inner() {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI constexpr auto& __update_inner() {
     if constexpr (__ref_is_glvalue)
       return std::__as_lvalue(*__get_outer());
     else
       return __parent_->__inner_.__emplace_from([&]() -> decltype(auto) { return *__get_outer(); });
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto& __get_inner() {
+  [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI constexpr auto& __get_inner() {
     if constexpr (__ref_is_glvalue)
       return std::__as_lvalue(*__get_outer());
     else
@@ -283,7 +283,7 @@ struct join_with_view<_View, _Pattern>::__iterator
     }
   }
 
-  static consteval auto __get_iterator_concept() noexcept {
+  [[__nodiscard__]] static consteval auto __get_iterator_concept() noexcept {
     if constexpr (__ref_is_glvalue && bidirectional_range<_Base> && __bidirectional_common<_InnerBase> &&
                   __bidirectional_common<_PatternBase>)
       return bidirectional_iterator_tag{};

>From b4afb2cafc7e58fe7b27ba742742181f671f1948 Mon Sep 17 00:00:00 2001
From: Jakub Mazurkiewicz <mazkuba3 at gmail.com>
Date: Thu, 21 Nov 2024 16:10:37 +0100
Subject: [PATCH 03/15] Add third template argument in
 `__join_with_view_iterator_category`

Comment: https://github.com/llvm/llvm-project/pull/65536#discussion_r1779496537
---
 libcxx/include/__ranges/join_with_view.h | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/libcxx/include/__ranges/join_with_view.h b/libcxx/include/__ranges/join_with_view.h
index d27f0fb168dc9e..4c1d255bd84b93 100644
--- a/libcxx/include/__ranges/join_with_view.h
+++ b/libcxx/include/__ranges/join_with_view.h
@@ -153,24 +153,24 @@ template <input_range _Range>
 join_with_view(_Range&&, range_value_t<range_reference_t<_Range>>)
     -> join_with_view<views::all_t<_Range>, single_view<range_value_t<range_reference_t<_Range>>>>;
 
-template <class _Base, class _PatternBase>
+template <class _Base, class _PatternBase, class _InnerBase = range_reference_t<_Base>>
 struct __join_with_view_iterator_category {};
 
-template <class _Base, class _PatternBase>
-  requires is_reference_v<range_reference_t<_Base>> && forward_range<_Base> && forward_range<range_reference_t<_Base>>
-struct __join_with_view_iterator_category<_Base, _PatternBase> {
+template <class _Base, class _PatternBase, class _InnerBase>
+  requires is_reference_v<range_reference_t<_Base>> && forward_range<_Base> && forward_range<_InnerBase>
+struct __join_with_view_iterator_category<_Base, _PatternBase, _InnerBase> {
 private:
   static consteval auto __get_iterator_category() noexcept {
     using _OuterC   = iterator_traits<iterator_t<_Base>>::iterator_category;
-    using _InnerC   = iterator_traits<iterator_t<range_reference_t<_Base>>>::iterator_category;
+    using _InnerC   = iterator_traits<iterator_t<_InnerBase>>::iterator_category;
     using _PatternC = iterator_traits<iterator_t<_PatternBase>>::iterator_category;
 
-    if constexpr (!is_reference_v<common_reference_t<iter_reference_t<iterator_t<range_reference_t<_Base>>>,
+    if constexpr (!is_reference_v<common_reference_t<iter_reference_t<iterator_t<_InnerBase>>,
                                                      iter_reference_t<iterator_t<_PatternBase>>>>)
       return input_iterator_tag{};
     else if constexpr (derived_from<_OuterC, bidirectional_iterator_tag> &&
                        derived_from<_InnerC, bidirectional_iterator_tag> &&
-                       derived_from<_PatternC, bidirectional_iterator_tag> && common_range<range_reference_t<_Base>> &&
+                       derived_from<_PatternC, bidirectional_iterator_tag> && common_range<_InnerBase> &&
                        common_range<_PatternBase>)
       return bidirectional_iterator_tag{};
     else if constexpr (derived_from<_OuterC, forward_iterator_tag> && derived_from<_InnerC, forward_iterator_tag> &&

>From 75230234d06067fef759565afd925f56c1af5c58 Mon Sep 17 00:00:00 2001
From: Jakub Mazurkiewicz <mazkuba3 at gmail.com>
Date: Wed, 27 Nov 2024 15:51:36 +0100
Subject: [PATCH 04/15] Add missing <includes>

---
 libcxx/include/__ranges/join_with_view.h                       | 1 +
 .../no_unique_address.compile.pass.cpp                         | 3 ++-
 .../range.join.with/range.join.with.overview/adaptor.pass.cpp  | 1 +
 3 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/join_with_view.h b/libcxx/include/__ranges/join_with_view.h
index 4c1d255bd84b93..3f687664225ba9 100644
--- a/libcxx/include/__ranges/join_with_view.h
+++ b/libcxx/include/__ranges/join_with_view.h
@@ -15,6 +15,7 @@
 #include <__concepts/constructible.h>
 #include <__concepts/convertible_to.h>
 #include <__concepts/derived_from.h>
+#include <__concepts/equality_comparable.h>
 #include <__config>
 #include <__functional/bind_back.h>
 #include <__iterator/concepts.h>
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/no_unique_address.compile.pass.cpp
index f1d310c3792ee8..83682c6ef20c5f 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/no_unique_address.compile.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/no_unique_address.compile.pass.cpp
@@ -13,8 +13,9 @@
 
 // This test ensures that we use `[[no_unique_address]]` in `join_with_view::iterator`.
 
-#include <iterator>
+#include <cstddef>
 #include <ranges>
+#include <variant>
 
 struct IntRange : std::ranges::view_base {
   int* begin();
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.overview/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.overview/adaptor.pass.cpp
index 82469a146f00b2..e92e8a1dcfbe8d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.overview/adaptor.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.overview/adaptor.pass.cpp
@@ -14,6 +14,7 @@
 
 #include <ranges>
 
+#include <memory>
 #include <span>
 #include <string_view>
 #include <utility>

>From 571498372eae18ade3482c997dbbc9d3d21dc77d Mon Sep 17 00:00:00 2001
From: Jakub Mazurkiewicz <mazkuba3 at gmail.com>
Date: Wed, 27 Nov 2024 15:52:01 +0100
Subject: [PATCH 05/15] Clang 17 is no longer supported

---
 .../range.join.with.iterator/no_unique_address.compile.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/no_unique_address.compile.pass.cpp
index 83682c6ef20c5f..2dfac0d5e15549 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/no_unique_address.compile.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/no_unique_address.compile.pass.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-// XFAIL: msvc && clang-17
+// XFAIL: msvc
 
 // <ranges>
 

>From 67784267be6c04786ff9bed56026e2b63134b2f0 Mon Sep 17 00:00:00 2001
From: Jakub Mazurkiewicz <mazkuba3 at gmail.com>
Date: Wed, 27 Nov 2024 23:10:33 +0100
Subject: [PATCH 06/15] Implement LWG-4074

---
 libcxx/docs/Status/Cxx2cIssues.csv            |   2 +-
 libcxx/include/__ranges/concepts.h            |  40 +++++++
 libcxx/include/__ranges/join_with_view.h      |  18 ++-
 .../range.join.with.view/begin.pass.cpp       |   5 +
 .../constraints.compile.pass.cpp              | 103 +++++++++++-------
 .../range.join.with.view/end.pass.cpp         |   4 +
 .../range.adaptors/range.join.with/types.h    |  56 +++++++++-
 7 files changed, 174 insertions(+), 54 deletions(-)

diff --git a/libcxx/docs/Status/Cxx2cIssues.csv b/libcxx/docs/Status/Cxx2cIssues.csv
index a0d2ccdc34d08d..c87fe84762ef0d 100644
--- a/libcxx/docs/Status/Cxx2cIssues.csv
+++ b/libcxx/docs/Status/Cxx2cIssues.csv
@@ -66,7 +66,7 @@
 "`LWG4060 <https://wg21.link/LWG4060>`__","``submdspan`` preconditions do not forbid creating invalid pointer","2024-06 (St. Louis)","","",""
 "`LWG4061 <https://wg21.link/LWG4061>`__","Should ``std::basic_format_context`` be default-constructible/copyable/movable?","2024-06 (St. Louis)","|Complete|","19",""
 "`LWG4071 <https://wg21.link/LWG4071>`__","``reference_wrapper`` comparisons are not SFINAE-friendly","2024-06 (St. Louis)","|Complete|","19",""
-"`LWG4074 <https://wg21.link/LWG4074>`__","``compatible-joinable-ranges`` is underconstrained","2024-06 (St. Louis)","","",""
+"`LWG4074 <https://wg21.link/LWG4074>`__","``compatible-joinable-ranges`` is underconstrained","2024-06 (St. Louis)","|Complete|","20",""
 "`LWG4076 <https://wg21.link/LWG4076>`__","``concat_view`` should be freestanding","2024-06 (St. Louis)","","",""
 "`LWG4079 <https://wg21.link/LWG4079>`__","Missing Preconditions in ``concat_view::iterator``\`s conversion constructor","2024-06 (St. Louis)","","",""
 "`LWG4082 <https://wg21.link/LWG4082>`__","``views::concat(r)`` is well-formed when ``r`` is an ``output_range``","2024-06 (St. Louis)","","",""
diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h
index 674a3f359ff99c..82d5b8fceaabfb 100644
--- a/libcxx/include/__ranges/concepts.h
+++ b/libcxx/include/__ranges/concepts.h
@@ -10,7 +10,9 @@
 #ifndef _LIBCPP___RANGES_CONCEPTS_H
 #define _LIBCPP___RANGES_CONCEPTS_H
 
+#include <__concepts/common_reference_with.h>
 #include <__concepts/constructible.h>
+#include <__concepts/convertible_to.h>
 #include <__concepts/movable.h>
 #include <__concepts/same_as.h>
 #include <__config>
@@ -25,6 +27,8 @@
 #include <__ranges/enable_view.h>
 #include <__ranges/size.h>
 #include <__type_traits/add_pointer.h>
+#include <__type_traits/common_reference.h>
+#include <__type_traits/common_type.h>
 #include <__type_traits/is_reference.h>
 #include <__type_traits/remove_cvref.h>
 #include <__type_traits/remove_reference.h>
@@ -133,6 +137,42 @@ concept viewable_range =
       (is_lvalue_reference_v<_Tp> ||
        (movable<remove_reference_t<_Tp>> && !__is_std_initializer_list<remove_cvref_t<_Tp>>))));
 
+#  if _LIBCPP_STD_VER >= 23
+
+template <class... _Rs>
+using __concat_reference_t = common_reference_t<range_reference_t<_Rs>...>;
+
+template <class... _Rs>
+using __concat_value_t = common_type_t<range_value_t<_Rs>...>;
+
+template <class... _Rs>
+using __concat_rvalue_reference_t = common_reference_t<range_rvalue_reference_t<_Rs>...>;
+
+template <class _Ref, class _RRef, class _It>
+concept __concat_indirectly_readable_impl = requires(const _It __it) {
+  { *__it } -> convertible_to<_Ref>;
+  { ranges::iter_move(__it) } -> convertible_to<_RRef>;
+};
+
+template <class... _Rs>
+concept __concat_indirectly_readable =
+    common_reference_with<__concat_reference_t<_Rs...>&&, __concat_value_t<_Rs...>&> &&
+    common_reference_with<__concat_reference_t<_Rs...>&&, __concat_rvalue_reference_t<_Rs...>&&> &&
+    common_reference_with<__concat_rvalue_reference_t<_Rs...>&&, const __concat_value_t<_Rs...>&> &&
+    (__concat_indirectly_readable_impl<__concat_reference_t<_Rs...>,
+                                       __concat_rvalue_reference_t<_Rs...>,
+                                       iterator_t<_Rs>> &&
+     ...);
+
+template <class... _Rs>
+concept __concatable = requires {
+  typename __concat_reference_t<_Rs...>;
+  typename __concat_value_t<_Rs...>;
+  typename __concat_rvalue_reference_t<_Rs...>;
+} && __concat_indirectly_readable<_Rs...>;
+
+#  endif // _LIBCPP_STD_VER >= 23
+
 } // namespace ranges
 
 #endif // _LIBCPP_STD_VER >= 20
diff --git a/libcxx/include/__ranges/join_with_view.h b/libcxx/include/__ranges/join_with_view.h
index 3f687664225ba9..e705f06a948fcf 100644
--- a/libcxx/include/__ranges/join_with_view.h
+++ b/libcxx/include/__ranges/join_with_view.h
@@ -54,18 +54,12 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 #if _LIBCPP_STD_VER >= 23
 
 namespace ranges {
-template <class _Range, class _Pattern>
-concept __compatible_joinable_ranges =
-    common_with<range_value_t<_Range>, range_value_t<_Pattern>> &&
-    common_reference_with<range_reference_t<_Range>, range_reference_t<_Pattern>> &&
-    common_reference_with<range_rvalue_reference_t<_Range>, range_rvalue_reference_t<_Pattern>>;
-
 template <class _Range>
 concept __bidirectional_common = bidirectional_range<_Range> && common_range<_Range>;
 
 template <input_range _View, forward_range _Pattern>
   requires view<_View> && input_range<range_reference_t<_View>> && view<_Pattern> &&
-           __compatible_joinable_ranges<range_reference_t<_View>, _Pattern>
+           __concatable<range_reference_t<_View>, _Pattern>
 class join_with_view : public view_interface<join_with_view<_View, _Pattern>> {
   using _InnerRng = range_reference_t<_View>;
 
@@ -121,7 +115,8 @@ class join_with_view : public view_interface<join_with_view<_View, _Pattern>> {
 
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires forward_range<const _View> && forward_range<const _Pattern> &&
-             is_reference_v<range_reference_t<const _View>> && input_range<range_reference_t<const _View>>
+             is_reference_v<range_reference_t<const _View>> && input_range<range_reference_t<const _View>> &&
+             __concatable<range_reference_t<const _View>, const _Pattern>
   {
     return __iterator<true>{*this, ranges::begin(__base_)};
   }
@@ -137,7 +132,8 @@ class join_with_view : public view_interface<join_with_view<_View, _Pattern>> {
 
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
     requires forward_range<const _View> && forward_range<const _Pattern> &&
-             is_reference_v<range_reference_t<const _View>> && input_range<range_reference_t<const _View>>
+             is_reference_v<range_reference_t<const _View>> && input_range<range_reference_t<const _View>> &&
+             __concatable<range_reference_t<const _View>, const _Pattern>
   {
     using _InnerConstRng = range_reference_t<const _View>;
     if constexpr (forward_range<_InnerConstRng> && common_range<const _View> && common_range<_InnerConstRng>)
@@ -187,7 +183,7 @@ struct __join_with_view_iterator_category<_Base, _PatternBase, _InnerBase> {
 
 template <input_range _View, forward_range _Pattern>
   requires view<_View> && input_range<range_reference_t<_View>> && view<_Pattern> &&
-           __compatible_joinable_ranges<range_reference_t<_View>, _Pattern>
+           __concatable<range_reference_t<_View>, _Pattern>
 template <bool _Const>
 struct join_with_view<_View, _Pattern>::__iterator
     : public __join_with_view_iterator_category<__maybe_const<_Const, _View>, __maybe_const<_Const, _Pattern>> {
@@ -395,7 +391,7 @@ struct join_with_view<_View, _Pattern>::__iterator
 
 template <input_range _View, forward_range _Pattern>
   requires view<_View> && input_range<range_reference_t<_View>> && view<_Pattern> &&
-           __compatible_joinable_ranges<range_reference_t<_View>, _Pattern>
+           __concatable<range_reference_t<_View>, _Pattern>
 template <bool _Const>
 struct join_with_view<_View, _Pattern>::__sentinel {
 private:
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/begin.pass.cpp
index 98b6775146dd35..70004394d28816 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/begin.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/begin.pass.cpp
@@ -240,6 +240,11 @@ constexpr void test_const_begin() {
     using JWV = std::ranges::join_with_view<MaybeSimpleForwardView<true>, MaybeSimplePattern<true>>;
     static_assert(std::same_as<std::ranges::iterator_t<JWV&>, std::ranges::iterator_t<const JWV&>>);
   }
+
+  { // Check LWG-4074: compatible-joinable-ranges is underconstrained
+    static_assert(!JoinWithViewHasConstBegin<BasicVectorView<int, ViewProperties{}, forward_iterator>,
+                                             lwg4074::PatternWithProxyConstAccess>);
+  }
 }
 
 constexpr bool test() {
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/constraints.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/constraints.compile.pass.cpp
index 5d446d9a0a60ed..cc528c468275ee 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/constraints.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/constraints.compile.pass.cpp
@@ -21,6 +21,7 @@
 #include <vector>
 
 #include "test_iterators.h"
+#include "../types.h"
 
 template <class View, class Pattern>
 concept CanFormJoinWithView = requires { typename std::ranges::join_with_view<View, Pattern>; };
@@ -133,7 +134,7 @@ static_assert(!CanFormJoinWithView<View, Pattern>);
 
 // join_with_view is not valid when `range_reference_t<View>` and pattern
 // does not model together compatible-joinable-ranges
-namespace test_when_used_ranges_are_not_compatible_joinable_ranges {
+namespace test_when_used_ranges_are_not_concatable {
 using std::ranges::range_reference_t;
 using std::ranges::range_rvalue_reference_t;
 using std::ranges::range_value_t;
@@ -144,15 +145,15 @@ struct View : std::ranges::view_base {
   InnerRange* end();
 };
 
-namespace no_common_range_value_type {
+namespace no_concat_reference_t {
+struct ValueType {};
+
 struct InnerRange {
   struct It {
-    using difference_type = ptrdiff_t;
-    struct value_type {};
-
+    using difference_type = std::ptrdiff_t;
+    using value_type      = ValueType;
     struct reference {
       operator value_type();
-      operator float();
     };
 
     It& operator++();
@@ -165,28 +166,42 @@ struct InnerRange {
 };
 
 struct Pattern : std::ranges::view_base {
-  const float* begin();
-  const float* end();
+  struct It {
+    using difference_type = std::ptrdiff_t;
+    using value_type      = ValueType;
+    struct reference {
+      operator value_type();
+    };
+
+    It& operator++();
+    It operator++(int);
+    reference operator*() const;
+    bool operator==(const It&) const;
+    friend value_type&& iter_move(const It&);
+  };
+
+  It begin();
+  It end();
 };
 
 static_assert(std::ranges::input_range<InnerRange>);
 static_assert(std::ranges::forward_range<Pattern>);
 static_assert(std::ranges::view<Pattern>);
-static_assert(!std::common_with<range_value_t<InnerRange>, range_value_t<Pattern>>);
-static_assert(std::common_reference_with<range_reference_t<InnerRange>, range_reference_t<Pattern>>);
+static_assert(!std::common_reference_with<range_reference_t<InnerRange>, range_reference_t<Pattern>>);
+static_assert(std::common_with<range_value_t<InnerRange>, range_value_t<Pattern>>);
 static_assert(std::common_reference_with<range_rvalue_reference_t<InnerRange>, range_rvalue_reference_t<Pattern>>);
 static_assert(!CanFormJoinWithView<View<InnerRange>, Pattern>);
-} // namespace no_common_range_value_type
-
-namespace no_common_range_reference_type {
-struct ValueType {};
+} // namespace no_concat_reference_t
 
+namespace no_concat_value_t {
 struct InnerRange {
   struct It {
-    using difference_type = ptrdiff_t;
-    using value_type      = ValueType;
+    using difference_type = std::ptrdiff_t;
+    struct value_type {};
+
     struct reference {
       operator value_type();
+      operator float();
     };
 
     It& operator++();
@@ -199,34 +214,20 @@ struct InnerRange {
 };
 
 struct Pattern : std::ranges::view_base {
-  struct It {
-    using difference_type = ptrdiff_t;
-    using value_type      = ValueType;
-    struct reference {
-      operator value_type();
-    };
-
-    It& operator++();
-    It operator++(int);
-    reference operator*() const;
-    bool operator==(const It&) const;
-    friend value_type&& iter_move(const It&);
-  };
-
-  It begin();
-  It end();
+  const float* begin();
+  const float* end();
 };
 
 static_assert(std::ranges::input_range<InnerRange>);
 static_assert(std::ranges::forward_range<Pattern>);
 static_assert(std::ranges::view<Pattern>);
-static_assert(std::common_with<range_value_t<InnerRange>, range_value_t<Pattern>>);
-static_assert(!std::common_reference_with<range_reference_t<InnerRange>, range_reference_t<Pattern>>);
+static_assert(std::common_reference_with<range_reference_t<InnerRange>, range_reference_t<Pattern>>);
+static_assert(!std::common_with<range_value_t<InnerRange>, range_value_t<Pattern>>);
 static_assert(std::common_reference_with<range_rvalue_reference_t<InnerRange>, range_rvalue_reference_t<Pattern>>);
 static_assert(!CanFormJoinWithView<View<InnerRange>, Pattern>);
-} // namespace no_common_range_reference_type
+} // namespace no_concat_value_t
 
-namespace no_common_range_rvalue_reference_type {
+namespace no_concat_rvalue_reference_t {
 struct InnerRange {
   using It = cpp20_input_iterator<int*>;
   It begin();
@@ -235,7 +236,7 @@ struct InnerRange {
 
 struct Pattern : std::ranges::view_base {
   struct It {
-    using difference_type = ptrdiff_t;
+    using difference_type = std::ptrdiff_t;
     struct value_type {
       operator int() const;
     };
@@ -258,9 +259,31 @@ struct Pattern : std::ranges::view_base {
 static_assert(std::ranges::input_range<InnerRange>);
 static_assert(std::ranges::forward_range<Pattern>);
 static_assert(std::ranges::view<Pattern>);
-static_assert(std::common_with<range_value_t<InnerRange>, range_value_t<Pattern>>);
 static_assert(std::common_reference_with<range_reference_t<InnerRange>, range_reference_t<Pattern>>);
+static_assert(std::common_with<range_value_t<InnerRange>, range_value_t<Pattern>>);
 static_assert(!std::common_reference_with<range_rvalue_reference_t<InnerRange>, range_rvalue_reference_t<Pattern>>);
 static_assert(!CanFormJoinWithView<View<InnerRange>, Pattern>);
-} // namespace no_common_range_rvalue_reference_type
-} // namespace test_when_used_ranges_are_not_compatible_joinable_ranges
+} // namespace no_concat_rvalue_reference_t
+
+namespace not_concat_indirectly_readable { // Required after LWG-4074 ("compatible-joinable-ranges is underconstrained")
+struct InnerRange {
+  using It = cpp20_input_iterator<int*>;
+  It begin();
+  sentinel_wrapper<It> end();
+};
+
+struct Pattern : std::ranges::view_base {
+  lwg4074::Iter begin();
+  lwg4074::Iter end();
+};
+
+static_assert(std::ranges::input_range<InnerRange>);
+static_assert(std::ranges::forward_range<Pattern>);
+static_assert(std::ranges::view<Pattern>);
+static_assert(std::common_reference_with<range_reference_t<InnerRange>, range_reference_t<Pattern>>);
+static_assert(std::common_with<range_value_t<InnerRange>, range_value_t<Pattern>>);
+static_assert(std::common_reference_with<range_rvalue_reference_t<InnerRange>, range_rvalue_reference_t<Pattern>>);
+LIBCPP_STATIC_ASSERT(!std::ranges::__concat_indirectly_readable<InnerRange, Pattern>);
+static_assert(!CanFormJoinWithView<View<InnerRange>, Pattern>);
+} // namespace not_concat_indirectly_readable
+} // namespace test_when_used_ranges_are_not_concatable
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/end.pass.cpp
index c6c54f74f6b77f..6334b2c9a96d15 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/end.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/end.pass.cpp
@@ -233,6 +233,10 @@ constexpr bool test() {
     static_assert(std::same_as<Sentinel, ConstSentinel>);
   }
 
+  // Check LWG-4074: compatible-joinable-ranges is underconstrained
+  static_assert(!JoinWithViewHasConstEnd<BasicVectorView<int, ViewProperties{}, forward_iterator>,
+                                         lwg4074::PatternWithProxyConstAccess>);
+
   return true;
 }
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/types.h b/libcxx/test/std/ranges/range.adaptors/range.join.with/types.h
index be551202966820..588c647cef23a7 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.with/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/types.h
@@ -9,6 +9,7 @@
 #ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_JOIN_WITH_TYPES_H
 #define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_JOIN_WITH_TYPES_H
 
+#include <cstddef>
 #include <initializer_list>
 #include <ranges>
 #include <string>
@@ -35,8 +36,7 @@ struct ViewProperties {
 
 template <std::ranges::input_range Data,
           ViewProperties Prop,
-          template <class...>
-          class It,
+          template <class...> class It,
           template <class...> class ConstIt = It>
 class BasicView : public std::ranges::view_base {
   Data data_;
@@ -204,6 +204,50 @@ struct ConstOppositeView : std::ranges::view_base {
   sentinel_wrapper<Val*> end() const;
 };
 
+namespace lwg4074 { // Helpers for LWG-4074 ("compatible-joinable-ranges is underconstrained")
+struct CommonReference;
+
+struct Value {
+  Value(int);
+};
+
+struct Reference {
+  operator Value() const;
+  operator CommonReference() const;
+};
+
+struct CommonReference {
+  CommonReference(int);
+};
+
+struct Iter {
+  using value_type      = Value;
+  using difference_type = std::ptrdiff_t;
+
+  Iter& operator++();
+  Iter operator++(int);
+  Reference operator*() const;
+  bool operator==(const Iter&) const;
+};
+
+struct PatternWithProxyConstAccess {
+  int* begin();
+  int* end();
+  Iter begin() const;
+  Iter end() const;
+};
+} // namespace lwg4074
+
+template <template <class> class TQual, template <class> class UQual>
+struct std::basic_common_reference<lwg4074::Reference, int, TQual, UQual> {
+  using type = lwg4074::CommonReference;
+};
+
+template <template <class> class TQual, template <class> class UQual>
+struct std::basic_common_reference<int, lwg4074::Reference, TQual, UQual> {
+  using type = lwg4074::CommonReference;
+};
+
 namespace selftest {
 using BV1 = BasicView<std::string, ViewProperties{.simple = true}, forward_iterator>;
 static_assert(std::ranges::forward_range<BV1>);
@@ -262,6 +306,14 @@ static_assert(!std::ranges::common_range<COV>);
 static_assert(!std::ranges::common_range<const COV>);
 static_assert(std::convertible_to<std::ranges::iterator_t<const COV>, std::ranges::iterator_t<COV>>);
 static_assert(!std::convertible_to<std::ranges::iterator_t<COV>, std::ranges::iterator_t<const COV>>);
+
+static_assert(std::common_with<lwg4074::Value, int>);
+static_assert(std::common_with<lwg4074::Value, lwg4074::Reference>);
+static_assert(std::common_reference_with<lwg4074::Reference, int&>);
+static_assert(std::common_reference_with<lwg4074::Reference, lwg4074::CommonReference>);
+static_assert(std::forward_iterator<lwg4074::Iter>);
+static_assert(std::ranges::forward_range<lwg4074::PatternWithProxyConstAccess>);
+static_assert(std::ranges::forward_range<const lwg4074::PatternWithProxyConstAccess>);
 } // namespace selftest
 
 #endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_JOIN_WITH_TYPES_H

>From 3a73facdb6d3f0cc3965eaf54e89578e6adf5685 Mon Sep 17 00:00:00 2001
From: Jakub Mazurkiewicz <mazkuba3 at gmail.com>
Date: Wed, 27 Nov 2024 23:40:54 +0100
Subject: [PATCH 07/15] Usa `[[no_unique_address]]` in
 `join_with_view::sentinel`

---
 libcxx/include/__ranges/join_with_view.h      |  2 +-
 .../no_unique_address.compile.pass.cpp        | 48 +++++++++++++++++++
 2 files changed, 49 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.sentinel/no_unique_address.compile.pass.cpp

diff --git a/libcxx/include/__ranges/join_with_view.h b/libcxx/include/__ranges/join_with_view.h
index e705f06a948fcf..9b0d2433aab035 100644
--- a/libcxx/include/__ranges/join_with_view.h
+++ b/libcxx/include/__ranges/join_with_view.h
@@ -400,7 +400,7 @@ struct join_with_view<_View, _Pattern>::__sentinel {
   using _Parent = __maybe_const<_Const, join_with_view>;
   using _Base   = __maybe_const<_Const, _View>;
 
-  sentinel_t<_Base> __end_ = sentinel_t<_Base>();
+  _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_Base> __end_ = sentinel_t<_Base>();
 
   _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_Parent& __parent) : __end_(ranges::end(__parent.__base_)) {}
 
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.sentinel/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.sentinel/no_unique_address.compile.pass.cpp
new file mode 100644
index 00000000000000..3d6d9c88c54409
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.sentinel/no_unique_address.compile.pass.cpp
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// XFAIL: msvc
+
+// <ranges>
+
+// This test ensures that we use `[[no_unique_address]]` in `join_with_view::sentinel`.
+
+#include <cstddef>
+#include <ranges>
+#include <string_view>
+
+template <bool Const>
+struct Iter {
+  using value_type      = std::string_view;
+  using difference_type = std::ptrdiff_t;
+
+  Iter& operator++();
+  Iter operator++(int);
+  value_type& operator*() const;
+  bool operator==(const Iter&) const;
+  bool operator==(std::default_sentinel_t) const;
+};
+
+struct View : std::ranges::view_base {
+  Iter<false> begin();
+  Iter<true> begin() const;
+  std::default_sentinel_t end() const;
+};
+
+using JWV = std::ranges::join_with_view<View, std::string_view>;
+
+template <class View>
+struct Test {
+  [[no_unique_address]] std::ranges::sentinel_t<View> se;
+  unsigned char pad;
+};
+
+static_assert(sizeof(Test<JWV>) == 1);
+static_assert(sizeof(Test<const JWV>) == 1);

>From f6f603d3765f97033d324ab476743e01968512c5 Mon Sep 17 00:00:00 2001
From: Jakub Mazurkiewicz <mazkuba3 at gmail.com>
Date: Thu, 28 Nov 2024 14:43:15 +0100
Subject: [PATCH 08/15] Cleanup `[[no_unique_address]]` tests

Related comments:
* https://github.com/llvm/llvm-project/pull/65536#discussion_r1779740468
* https://github.com/llvm/llvm-project/pull/65536#discussion_r1779740529
* https://github.com/llvm/llvm-project/pull/65536#discussion_r1780034366
---
 .../no_unique_address.compile.pass.cpp        | 29 +++++++++++--------
 .../no_unique_address.compile.pass.cpp        | 13 +++++++--
 2 files changed, 27 insertions(+), 15 deletions(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/no_unique_address.compile.pass.cpp
index 2dfac0d5e15549..25debc553ac2f8 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/no_unique_address.compile.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/no_unique_address.compile.pass.cpp
@@ -22,31 +22,36 @@ struct IntRange : std::ranges::view_base {
   int* end();
 };
 
-class InputIter {
+class Iter {
 public:
   using value_type      = IntRange;
   using difference_type = ptrdiff_t;
 
-  InputIter& operator++();
+  Iter& operator++();
   void operator++(int);
   value_type& operator*() const;
-  bool operator==(const InputIter&) const;
+  bool operator==(std::default_sentinel_t) const;
 
 private:
   int* ptr_;
 };
 
-static_assert(std::input_iterator<InputIter>);
-static_assert(!std::forward_iterator<InputIter>);
+static_assert(std::input_iterator<Iter>);
+static_assert(!std::forward_iterator<Iter>);
 
-struct InputView : std::ranges::view_base {
-  InputIter begin();
-  InputIter end();
+struct View : std::ranges::view_base {
+  Iter begin();
+  std::default_sentinel_t end();
 };
 
-static_assert(std::ranges::input_range<InputView>);
-static_assert(!std::ranges::forward_range<InputView>);
+static_assert(std::ranges::input_range<View>);
+static_assert(!std::ranges::forward_range<View>);
 
-static_assert(alignof(void*) == alignof(std::variant<int*, int*>)); // alignof(__parent_) == alignof(__inner_it_)
-static_assert(sizeof(std::ranges::iterator_t<std::ranges::join_with_view<InputView, IntRange>>) ==
+using JWV = std::ranges::join_with_view<View, IntRange>;
+
+// Expected JWV::iterator layout:
+// _Parent* __parent_;                           // offset: 0
+// [[no_unique_address]] __empty __outer_it;     //         0
+// variant<_PatternIter, _InnerIter> __pattern_; //         8
+static_assert(sizeof(std::ranges::iterator_t<JWV>) ==
               sizeof(void*) + sizeof(std::variant<int*, int*>)); // sizeof(__parent_) + sizeof(__inner_it_)
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/no_unique_address.compile.pass.cpp
index 495a453a898089..e9f630792d7593 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/no_unique_address.compile.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/no_unique_address.compile.pass.cpp
@@ -33,8 +33,15 @@ struct Pattern : std::ranges::view_base {
 template <class View>
 struct Test {
   [[no_unique_address]] View view;
-  char c;
+  unsigned char pad;
 };
 
-static_assert(sizeof(Test<std::ranges::join_with_view<ForwardView, Pattern>>) ==
-              sizeof(std::ranges::join_with_view<ForwardView, Pattern>));
+using JWV = std::ranges::join_with_view<ForwardView, Pattern>;
+
+// Expected JWV layout:
+// [[no_unique_address]] _View __base_             // offset: 0
+// [[no_unique_address]] __empty_cache __outer_it; //         0
+// [[no_unique_address]] __empty_cache __inner_;   //         1
+// [[no_unique_address]] _Patter __pattern_        //         0
+static_assert(sizeof(JWV) == 2);
+static_assert(sizeof(Test<JWV>) == 2);

>From e55c077058a39d1f123a1a8282c77b972ab9efad Mon Sep 17 00:00:00 2001
From: Jakub Mazurkiewicz <mazkuba3 at gmail.com>
Date: Thu, 28 Nov 2024 15:31:33 +0100
Subject: [PATCH 09/15] Test when result of `operator*` is type different from
 `range_reference_t<InnerRng>` and `range_reference_t<Pattern>`

Related comment: https://github.com/llvm/llvm-project/pull/65536#discussion_r1780064183
---
 .../range.join.with.iterator/deref.pass.cpp   | 81 +++++++++++++++++++
 1 file changed, 81 insertions(+)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/deref.pass.cpp
index 2491e8bf75f701..032141d0d47f7c 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/deref.pass.cpp
@@ -16,13 +16,72 @@
 
 #include <array>
 #include <cassert>
+#include <cstddef>
 #include <string>
 #include <string_view>
+#include <type_traits>
 #include <utility>
 #include <vector>
 
 #include "../types.h"
 
+struct ProxyInt {
+  int& val;
+};
+
+class ExtraProxyInt {
+public:
+  constexpr ExtraProxyInt(ProxyInt i) : val(i.val) {}
+  constexpr ExtraProxyInt(int i) : val(i) {}
+
+  constexpr int get() const { return val; }
+
+private:
+  int val;
+};
+
+template <template <class> class TQual, template <class> class UQual>
+struct std::basic_common_reference<ProxyInt, int, TQual, UQual> {
+  using type = ExtraProxyInt;
+};
+
+template <template <class> class TQual, template <class> class UQual>
+struct std::basic_common_reference<int, ProxyInt, TQual, UQual> {
+  using type = ExtraProxyInt;
+};
+
+static_assert(std::common_reference_with<int&, ProxyInt>);
+static_assert(std::common_reference_with<int&, ExtraProxyInt>);
+
+class ProxyIter {
+public:
+  using value_type      = int;
+  using difference_type = std::ptrdiff_t;
+
+  constexpr ProxyIter() : ptr_(nullptr) {}
+  constexpr explicit ProxyIter(int* p) : ptr_(p) {}
+
+  constexpr ProxyInt operator*() const { return ProxyInt{*ptr_}; }
+
+  constexpr ProxyIter& operator++() {
+    ++ptr_;
+    return *this;
+  }
+
+  constexpr ProxyIter operator++(int) {
+    ProxyIter tmp = *this;
+    ++ptr_;
+    return tmp;
+  }
+
+  constexpr bool operator==(const ProxyIter& other) const { return ptr_ == other.ptr_; }
+
+private:
+  int* ptr_;
+};
+
+static_assert(std::forward_iterator<ProxyIter>);
+
 constexpr bool test() {
   { // Result of `operator*` is (maybe const) lvalue reference
     using V       = std::ranges::owning_view<std::vector<std::string>>;
@@ -133,6 +192,28 @@ constexpr bool test() {
     }
   }
 
+  { // Result of `operator*` is type different from range_reference_t<InnerRng> and range_reference_t<Pattern>
+    using Inner   = std::vector<int>;
+    using V       = std::vector<Inner>;
+    using Pattern = std::ranges::subrange<ProxyIter, ProxyIter>;
+    using JWV     = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;
+
+    static_assert(!std::same_as<std::ranges::range_reference_t<V>, std::ranges::range_reference_t<JWV>>);
+    static_assert(!std::same_as<std::ranges::range_reference_t<Pattern>, std::ranges::range_reference_t<JWV>>);
+
+    std::array<int, 2> pattern = {-1, -1};
+    Pattern pattern_as_subrange(ProxyIter{pattern.data()}, ProxyIter{pattern.data() + pattern.size()});
+
+    JWV jwv(V{Inner{1, 1}, Inner{2, 2}, Inner{3, 3}}, std::move(pattern_as_subrange));
+
+    auto it                                          = jwv.begin();
+    std::same_as<ExtraProxyInt> decltype(auto) v_ref = *it;
+    assert(v_ref.get() == 1);
+    std::ranges::advance(it, 7);
+    std::same_as<ExtraProxyInt> decltype(auto) pattern_ref = *std::as_const(it);
+    assert(pattern_ref.get() == -1);
+  }
+
   return true;
 }
 

>From 5a3996a2d4e8f40a7034d6133e3012b620599e55 Mon Sep 17 00:00:00 2001
From: Jakub Mazurkiewicz <mazkuba3 at gmail.com>
Date: Fri, 29 Nov 2024 00:09:37 +0100
Subject: [PATCH 10/15] Various cleanups

---
 libcxx/include/__ranges/join_with_view.h      |   2 +-
 .../no_unique_address.compile.pass.cpp        |   2 +-
 .../range.join.with.iterator/deref.pass.cpp   |  32 +--
 .../iter_move.pass.cpp                        | 199 +++++++++---------
 .../iter_swap.pass.cpp                        |   5 +-
 .../range.join.with.overview/example.pass.cpp |   6 +-
 .../range.join.with.view/base.pass.cpp        |  36 +---
 .../range.join.with.view/begin.pass.cpp       | 137 +++++-------
 .../range.join.with.view/end.pass.cpp         |  18 +-
 9 files changed, 183 insertions(+), 254 deletions(-)

diff --git a/libcxx/include/__ranges/join_with_view.h b/libcxx/include/__ranges/join_with_view.h
index 9b0d2433aab035..8f9ca554f809c3 100644
--- a/libcxx/include/__ranges/join_with_view.h
+++ b/libcxx/include/__ranges/join_with_view.h
@@ -247,7 +247,7 @@ struct join_with_view<_View, _Pattern>::__iterator
     if constexpr (__ref_is_glvalue)
       return std::__as_lvalue(*__get_outer());
     else
-      return __parent_->__inner_.__emplace_from([&]() -> decltype(auto) { return *__get_outer(); });
+      return __parent_->__inner_.__emplace_from([this]() -> decltype(auto) { return *__get_outer(); });
   }
 
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI constexpr auto& __get_inner() {
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/no_unique_address.compile.pass.cpp
index 25debc553ac2f8..ba1d233f5e33e3 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/no_unique_address.compile.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/no_unique_address.compile.pass.cpp
@@ -52,6 +52,6 @@ using JWV = std::ranges::join_with_view<View, IntRange>;
 // Expected JWV::iterator layout:
 // _Parent* __parent_;                           // offset: 0
 // [[no_unique_address]] __empty __outer_it;     //         0
-// variant<_PatternIter, _InnerIter> __pattern_; //         8
+// variant<_PatternIter, _InnerIter> __pattern_; //         sizeof(pointer)
 static_assert(sizeof(std::ranges::iterator_t<JWV>) ==
               sizeof(void*) + sizeof(std::variant<int*, int*>)); // sizeof(__parent_) + sizeof(__inner_it_)
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/deref.pass.cpp
index 032141d0d47f7c..94eb379438e652 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/deref.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/deref.pass.cpp
@@ -25,14 +25,14 @@
 
 #include "../types.h"
 
-struct ProxyInt {
+struct ProxyRef {
   int& val;
 };
 
-class ExtraProxyInt {
+class CommonProxyRef {
 public:
-  constexpr ExtraProxyInt(ProxyInt i) : val(i.val) {}
-  constexpr ExtraProxyInt(int i) : val(i) {}
+  constexpr CommonProxyRef(ProxyRef i) : val(i.val) {}
+  constexpr CommonProxyRef(int i) : val(i) {}
 
   constexpr int get() const { return val; }
 
@@ -41,17 +41,17 @@ class ExtraProxyInt {
 };
 
 template <template <class> class TQual, template <class> class UQual>
-struct std::basic_common_reference<ProxyInt, int, TQual, UQual> {
-  using type = ExtraProxyInt;
+struct std::basic_common_reference<ProxyRef, int, TQual, UQual> {
+  using type = CommonProxyRef;
 };
 
 template <template <class> class TQual, template <class> class UQual>
-struct std::basic_common_reference<int, ProxyInt, TQual, UQual> {
-  using type = ExtraProxyInt;
+struct std::basic_common_reference<int, ProxyRef, TQual, UQual> {
+  using type = CommonProxyRef;
 };
 
-static_assert(std::common_reference_with<int&, ProxyInt>);
-static_assert(std::common_reference_with<int&, ExtraProxyInt>);
+static_assert(std::common_reference_with<int&, ProxyRef>);
+static_assert(std::common_reference_with<int&, CommonProxyRef>);
 
 class ProxyIter {
 public:
@@ -61,7 +61,7 @@ class ProxyIter {
   constexpr ProxyIter() : ptr_(nullptr) {}
   constexpr explicit ProxyIter(int* p) : ptr_(p) {}
 
-  constexpr ProxyInt operator*() const { return ProxyInt{*ptr_}; }
+  constexpr ProxyRef operator*() const { return ProxyRef{*ptr_}; }
 
   constexpr ProxyIter& operator++() {
     ++ptr_;
@@ -196,7 +196,7 @@ constexpr bool test() {
     using Inner   = std::vector<int>;
     using V       = std::vector<Inner>;
     using Pattern = std::ranges::subrange<ProxyIter, ProxyIter>;
-    using JWV     = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;
+    using JWV     = std::ranges::join_with_view<std::ranges::owning_view<V>, Pattern>;
 
     static_assert(!std::same_as<std::ranges::range_reference_t<V>, std::ranges::range_reference_t<JWV>>);
     static_assert(!std::same_as<std::ranges::range_reference_t<Pattern>, std::ranges::range_reference_t<JWV>>);
@@ -204,13 +204,13 @@ constexpr bool test() {
     std::array<int, 2> pattern = {-1, -1};
     Pattern pattern_as_subrange(ProxyIter{pattern.data()}, ProxyIter{pattern.data() + pattern.size()});
 
-    JWV jwv(V{Inner{1, 1}, Inner{2, 2}, Inner{3, 3}}, std::move(pattern_as_subrange));
+    JWV jwv(V{Inner{1, 1}, Inner{2, 2}, Inner{3, 3}}, pattern_as_subrange);
 
-    auto it                                          = jwv.begin();
-    std::same_as<ExtraProxyInt> decltype(auto) v_ref = *it;
+    auto it                                           = jwv.begin();
+    std::same_as<CommonProxyRef> decltype(auto) v_ref = *it;
     assert(v_ref.get() == 1);
     std::ranges::advance(it, 7);
-    std::same_as<ExtraProxyInt> decltype(auto) pattern_ref = *std::as_const(it);
+    std::same_as<CommonProxyRef> decltype(auto) pattern_ref = *std::as_const(it);
     assert(pattern_ref.get() == -1);
   }
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_move.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_move.pass.cpp
index 147626f1a7ec34..b87d01ae36ad9a 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_move.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_move.pass.cpp
@@ -10,19 +10,14 @@
 
 // <ranges>
 
-// friend constexpr decltype(auto) iter_move(const iterator& x) {
-//   using rvalue_reference = common_reference_t<
-//     iter_rvalue_reference_t<InnerIter>,
-//     iter_rvalue_reference_t<PatternIter>>;
-//   return visit<rvalue_reference>(ranges::iter_move, x.inner_it_);
-// }
+// friend constexpr decltype(auto) iter_move(const iterator& x);
 
 #include <ranges>
 
 #include <algorithm>
 #include <array>
 #include <cassert>
-#include <type_traits>
+#include <cstddef>
 #include <utility>
 #include <vector>
 
@@ -52,7 +47,9 @@ class MoveOnlyInt {
     return *this;
   }
 
-  constexpr Status get_status() const { return status_; }
+  constexpr bool was_normally_constructed() const { return status_ == constructed; }
+  constexpr bool was_move_constructed() const { return status_ == move_constructed; }
+  constexpr bool was_moved_from() const { return status_ == moved_from_this; }
 
   friend constexpr bool operator==(const MoveOnlyInt& left, int right) { return left.val_ == right; }
   friend constexpr bool operator==(const MoveOnlyInt& left, const MoveOnlyInt& right) {
@@ -66,68 +63,77 @@ class MoveOnlyInt {
 
 static_assert(std::movable<MoveOnlyInt>);
 
-template <class T>
-class ProxyRvalue {
-  T val_;
+struct ProxyRvalueRef {
+  MoveOnlyInt&& val;
+};
 
+class CommonProxyRvalueRef {
 public:
-  constexpr ProxyRvalue(T val) : val_(std::move(val)) {}
+  constexpr CommonProxyRvalueRef(ProxyRvalueRef i) : val_(std::move(i.val)) {}
+  constexpr CommonProxyRvalueRef(MoveOnlyInt i) : val_(std::move(i)) {}
+
+  constexpr MoveOnlyInt&& get() { return std::move(val_); }
 
-  ProxyRvalue(ProxyRvalue&&)            = default;
-  ProxyRvalue& operator=(ProxyRvalue&&) = default;
+private:
+  MoveOnlyInt val_;
+};
 
-  constexpr explicit operator T&&() noexcept { return std::move(val_); }
+template <template <class> class TQual, template <class> class UQual>
+struct std::basic_common_reference<ProxyRvalueRef, MoveOnlyInt, TQual, UQual> {
+  using type = CommonProxyRvalueRef;
 };
 
-static_assert(std::common_reference_with<ProxyRvalue<int>, int>);
-static_assert(std::common_reference_with<ProxyRvalue<MoveOnlyInt>, MoveOnlyInt>);
+template <template <class> class TQual, template <class> class UQual>
+struct std::basic_common_reference<MoveOnlyInt, ProxyRvalueRef, TQual, UQual> {
+  using type = CommonProxyRvalueRef;
+};
 
-template <std::bidirectional_iterator It>
-class ProxyOnIterMoveIter {
-  It it_ = It();
+static_assert(std::common_reference_with<MoveOnlyInt&&, ProxyRvalueRef>);
+static_assert(std::common_reference_with<MoveOnlyInt&&, CommonProxyRvalueRef>);
 
+class ProxyIter {
 public:
-  using value_type      = std::iter_value_t<It>;
-  using difference_type = std::iter_difference_t<It>;
+  using value_type      = MoveOnlyInt;
+  using difference_type = std::ptrdiff_t;
 
-  ProxyOnIterMoveIter() = default;
-  constexpr ProxyOnIterMoveIter(It it) : it_(std::move(it)) {}
+  constexpr ProxyIter() : ptr_(nullptr) {}
+  constexpr explicit ProxyIter(MoveOnlyInt* it) : ptr_(std::move(it)) {}
 
-  constexpr decltype(auto) operator*() const { return *it_; }
+  constexpr decltype(auto) operator*() const { return *ptr_; }
 
-  constexpr ProxyOnIterMoveIter& operator++() {
-    ++it_;
+  constexpr ProxyIter& operator++() {
+    ++ptr_;
     return *this;
   }
 
-  constexpr ProxyOnIterMoveIter operator++(int) {
-    ProxyOnIterMoveIter copy = *this;
-    ++it_;
+  constexpr ProxyIter operator++(int) {
+    ProxyIter copy = *this;
+    ++ptr_;
     return copy;
   }
 
-  constexpr ProxyOnIterMoveIter& operator--() {
-    --it_;
+  constexpr ProxyIter& operator--() {
+    --ptr_;
     return *this;
   }
 
-  constexpr ProxyOnIterMoveIter operator--(int) {
-    ProxyOnIterMoveIter copy = *this;
-    --it_;
+  constexpr ProxyIter operator--(int) {
+    ProxyIter copy = *this;
+    --ptr_;
     return copy;
   }
 
-  friend bool operator==(const ProxyOnIterMoveIter&, const ProxyOnIterMoveIter&) = default;
+  friend bool operator==(const ProxyIter&, const ProxyIter&) = default;
 
-  friend constexpr ProxyRvalue<value_type> iter_move(const ProxyOnIterMoveIter iter) {
-    return ProxyRvalue<value_type>{std::ranges::iter_move(iter.it_)};
+  friend constexpr ProxyRvalueRef iter_move(const ProxyIter iter) {
+    return ProxyRvalueRef{std::ranges::iter_move(iter.ptr_)};
   }
-};
 
-template <class It>
-ProxyOnIterMoveIter(It) -> ProxyOnIterMoveIter<It>;
+private:
+  MoveOnlyInt* ptr_;
+};
 
-static_assert(std::bidirectional_iterator<ProxyOnIterMoveIter<int*>>);
+static_assert(std::forward_iterator<ProxyIter>);
 
 constexpr bool test() {
   { // Test `iter_move` when result is true rvalue reference. Test return types.
@@ -183,10 +189,9 @@ constexpr bool test() {
     v.emplace_back(std::ranges::to<Inner>(std::views::iota(0, 4)));
     v.emplace_back(std::ranges::to<Inner>(std::views::iota(12, 16)));
     JWV jwv(std::move(v), std::ranges::to<Pattern>(std::views::iota(4, 12)));
-    assert(std::ranges::all_of(jwv, [](const MoveOnlyInt& i) { return i.get_status() == MoveOnlyInt::constructed; }));
+    assert(std::ranges::all_of(jwv, &MoveOnlyInt::was_normally_constructed));
 
     {
-      using enum MoveOnlyInt::Status;
       std::vector<MoveOnlyInt> values;
       values.reserve(8);
 
@@ -208,11 +213,10 @@ constexpr bool test() {
       values.emplace_back(std::ranges::iter_move(std::as_const(it)));
 
       assert(std::ranges::equal(values, std::views::iota(0, 8)));
-      assert(std::ranges::all_of(values, [](const MoveOnlyInt& i) { return i.get_status() == move_constructed; }));
+      assert(std::ranges::all_of(values, &MoveOnlyInt::was_move_constructed));
     }
 
     {
-      using enum MoveOnlyInt::Status;
       std::vector<MoveOnlyInt> values;
       values.reserve(8);
 
@@ -234,99 +238,102 @@ constexpr bool test() {
       values.emplace_back(std::ranges::iter_move(std::as_const(cit)));
 
       assert(std::ranges::equal(std::views::reverse(values), std::views::iota(8, 16)));
-      assert(std::ranges::all_of(values, [](const MoveOnlyInt& i) { return i.get_status() == move_constructed; }));
+      assert(std::ranges::all_of(values, &MoveOnlyInt::was_move_constructed));
     }
 
-    assert(
-        std::ranges::all_of(jwv, [](const MoveOnlyInt& i) { return i.get_status() == MoveOnlyInt::moved_from_this; }));
+    assert(std::ranges::all_of(jwv, &MoveOnlyInt::was_moved_from));
   }
 
-  { // Test `iter_move` when result is proxy rvalue reference. Test return types and moving.
+  { // Test `iter_move` when result is proxy rvalue reference type, which is different from
+    // range_rvalue_reference_t<InnerRng> and range_rvalue_reference_t<Pattern>.
     using Inner   = std::vector<MoveOnlyInt>;
     using V       = std::vector<Inner>;
-    using Pattern = BasicVectorView<MoveOnlyInt, ViewProperties{}, ProxyOnIterMoveIter>;
-    using JWV     = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;
+    using Pattern = std::ranges::subrange<ProxyIter, ProxyIter>;
+    using JWV     = std::ranges::join_with_view<std::ranges::owning_view<V>, Pattern>;
 
-    using RRef = ProxyRvalue<MoveOnlyInt>;
-    static_assert(std::same_as<RRef, std::ranges::range_rvalue_reference_t<JWV>>);
+    static_assert(!std::same_as<std::ranges::range_rvalue_reference_t<V>, std::ranges::range_rvalue_reference_t<JWV>>);
+    static_assert(
+        !std::same_as<std::ranges::range_rvalue_reference_t<Pattern>, std::ranges::range_rvalue_reference_t<JWV>>);
+    static_assert(std::same_as<CommonProxyRvalueRef, std::ranges::range_rvalue_reference_t<JWV>>);
 
     V v;
     v.reserve(2);
     v.emplace_back(std::ranges::to<Inner>(std::views::iota(0, 4)));
     v.emplace_back(std::ranges::to<Inner>(std::views::iota(12, 16)));
-    JWV jwv(std::move(v), Pattern{std::ranges::to<std::vector<MoveOnlyInt>>(std::views::iota(4, 12))});
-    assert(std::ranges::all_of(jwv, [](const MoveOnlyInt& i) { return i.get_status() == MoveOnlyInt::constructed; }));
+
+    auto pattern = std::ranges::to<std::vector<MoveOnlyInt>>(std::views::iota(4, 12));
+    Pattern pattern_as_subrange(ProxyIter{pattern.data()}, ProxyIter{pattern.data() + pattern.size()});
+
+    JWV jwv(std::move(v), pattern_as_subrange);
+    assert(std::ranges::all_of(jwv, &MoveOnlyInt::was_normally_constructed));
 
     {
-      using enum MoveOnlyInt::Status;
       std::vector<MoveOnlyInt> values;
       values.reserve(8);
 
-      auto it                                 = jwv.begin();
-      std::same_as<RRef> decltype(auto) rref1 = iter_move(it);
-      values.emplace_back(std::move(rref1));
+      auto it                                                 = jwv.begin();
+      std::same_as<CommonProxyRvalueRef> decltype(auto) rref1 = iter_move(it);
+      values.emplace_back(rref1.get());
       ++it;
-      std::same_as<RRef> decltype(auto) rref2 = iter_move(std::as_const(it));
-      values.emplace_back(rref2);
+      std::same_as<CommonProxyRvalueRef> decltype(auto) rref2 = iter_move(std::as_const(it));
+      values.emplace_back(rref2.get());
       it++;
-      std::same_as<RRef> decltype(auto) rref3 = std::ranges::iter_move(it);
-      values.emplace_back(rref3);
+      std::same_as<CommonProxyRvalueRef> decltype(auto) rref3 = std::ranges::iter_move(it);
+      values.emplace_back(rref3.get());
       ++it;
-      std::same_as<RRef> decltype(auto) rref4 = std::ranges::iter_move(std::as_const(it));
-      values.emplace_back(rref4);
+      std::same_as<CommonProxyRvalueRef> decltype(auto) rref4 = std::ranges::iter_move(std::as_const(it));
+      values.emplace_back(rref4.get());
       it++; // `it` points to element of `Pattern` from here
-      std::same_as<RRef> decltype(auto) rref5 = iter_move(it);
-      values.emplace_back(rref5);
+      std::same_as<CommonProxyRvalueRef> decltype(auto) rref5 = iter_move(it);
+      values.emplace_back(rref5.get());
       ++it;
-      std::same_as<RRef> decltype(auto) rref6 = iter_move(std::as_const(it));
-      values.emplace_back(rref6);
+      std::same_as<CommonProxyRvalueRef> decltype(auto) rref6 = iter_move(std::as_const(it));
+      values.emplace_back(rref6.get());
       it++;
-      std::same_as<RRef> decltype(auto) rref7 = std::ranges::iter_move(it);
-      values.emplace_back(rref7);
+      std::same_as<CommonProxyRvalueRef> decltype(auto) rref7 = std::ranges::iter_move(it);
+      values.emplace_back(rref7.get());
       ++it;
-      std::same_as<RRef> decltype(auto) rref8 = std::ranges::iter_move(std::as_const(it));
-      values.emplace_back(rref8);
+      std::same_as<CommonProxyRvalueRef> decltype(auto) rref8 = std::ranges::iter_move(std::as_const(it));
+      values.emplace_back(rref8.get());
 
       assert(std::ranges::equal(values, std::views::iota(0, 8)));
-      assert(std::ranges::all_of(values, [](const MoveOnlyInt& i) { return i.get_status() == move_constructed; }));
+      assert(std::ranges::all_of(values, &MoveOnlyInt::was_move_constructed));
     }
 
     {
-      using enum MoveOnlyInt::Status;
       std::vector<MoveOnlyInt> values;
       values.reserve(8);
 
-      auto cit                                = std::prev(std::as_const(jwv).end());
-      std::same_as<RRef> decltype(auto) rref1 = iter_move(cit);
-      values.emplace_back(rref1);
+      auto cit                                                = std::prev(std::as_const(jwv).end());
+      std::same_as<CommonProxyRvalueRef> decltype(auto) rref1 = iter_move(cit);
+      values.emplace_back(rref1.get());
       cit--;
-      std::same_as<RRef> decltype(auto) rref2 = iter_move(std::as_const(cit));
-      values.emplace_back(rref2);
+      std::same_as<CommonProxyRvalueRef> decltype(auto) rref2 = iter_move(std::as_const(cit));
+      values.emplace_back(rref2.get());
       --cit;
-      std::same_as<RRef> decltype(auto) rref3 = std::ranges::iter_move(cit);
-      values.emplace_back(rref3);
+      std::same_as<CommonProxyRvalueRef> decltype(auto) rref3 = std::ranges::iter_move(cit);
+      values.emplace_back(rref3.get());
       cit--;
-      std::same_as<RRef> decltype(auto) rref4 = std::ranges::iter_move(std::as_const(cit));
-      values.emplace_back(rref4);
+      std::same_as<CommonProxyRvalueRef> decltype(auto) rref4 = std::ranges::iter_move(std::as_const(cit));
+      values.emplace_back(rref4.get());
       --cit; // `it` points to element of `Pattern` from here
-      std::same_as<RRef> decltype(auto) rref5 = iter_move(cit);
-      values.emplace_back(rref5);
+      std::same_as<CommonProxyRvalueRef> decltype(auto) rref5 = iter_move(cit);
+      values.emplace_back(rref5.get());
       cit--;
-      std::same_as<RRef> decltype(auto) rref6 = iter_move(std::as_const(cit));
-      values.emplace_back(rref6);
+      std::same_as<CommonProxyRvalueRef> decltype(auto) rref6 = iter_move(std::as_const(cit));
+      values.emplace_back(rref6.get());
       --cit;
-      std::same_as<RRef> decltype(auto) rref7 = std::ranges::iter_move(cit);
-      values.emplace_back(rref7);
+      std::same_as<CommonProxyRvalueRef> decltype(auto) rref7 = std::ranges::iter_move(cit);
+      values.emplace_back(rref7.get());
       cit--;
-      std::same_as<RRef> decltype(auto) rref8 = std::ranges::iter_move(std::as_const(cit));
-      values.emplace_back(rref8);
+      std::same_as<CommonProxyRvalueRef> decltype(auto) rref8 = std::ranges::iter_move(std::as_const(cit));
+      values.emplace_back(rref8.get());
 
       assert(std::ranges::equal(std::views::reverse(values), std::views::iota(8, 16)));
-      assert(std::ranges::all_of(values, [](const MoveOnlyInt& i) { return i.get_status() == move_constructed; }));
+      assert(std::ranges::all_of(values, &MoveOnlyInt::was_move_constructed));
     }
 
-    assert(
-        std::ranges::all_of(jwv, [](const MoveOnlyInt& i) { return i.get_status() == MoveOnlyInt::moved_from_this; }));
+    assert(std::ranges::all_of(jwv, &MoveOnlyInt::was_moved_from));
   }
 
   return true;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_swap.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_swap.pass.cpp
index b4e66b92b38833..cba8bc4efaf957 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_swap.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_swap.pass.cpp
@@ -10,10 +10,7 @@
 
 // <ranges>
 
-// friend constexpr void iter_swap(const iterator& x, const iterator& y)
-//   requires indirectly_swappable<InnerIter, PatternIter> {
-//   visit(ranges::iter_swap, x.inner_it_, y.inner_it_);
-// }
+// friend constexpr void iter_swap(const iterator& x, const iterator& y);
 
 #include <ranges>
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.overview/example.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.overview/example.pass.cpp
index e2409c67327e91..a4b05e598196bd 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.overview/example.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.overview/example.pass.cpp
@@ -20,13 +20,9 @@
 
 #include <ranges>
 
-#include <algorithm>
 #include <string>
-#include <string_view>
 #include <vector>
 
-using namespace std::string_view_literals;
-
 constexpr bool test() {
   std::vector<std::string> vs = {"the", "quick", "brown", "fox"};
   std::string result;
@@ -34,7 +30,7 @@ constexpr bool test() {
     result += c;
   }
 
-  return std::ranges::equal(result, "the-quick-brown-fox"sv);
+  return result == "the-quick-brown-fox";
 }
 
 int main(int, char**) {
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/base.pass.cpp
index eb6731443283d0..35769cecbc562c 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/base.pass.cpp
@@ -68,8 +68,7 @@ constexpr bool test() {
   InnerRange buff[3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
   Pattern pattern;
 
-  // Check the const& overload
-  {
+  { // Check the const& overload
     Range range(buff, buff + 3);
     std::ranges::join_with_view<Range, Pattern> view(range, pattern);
     std::same_as<Range> decltype(auto) result = view.base();
@@ -78,8 +77,7 @@ constexpr bool test() {
     assert(result.end() == buff + 3);
   }
 
-  // Check the const& overload on const `view`
-  {
+  { // Check the const& overload on const `view`
     Range range(buff, buff + 3);
     const std::ranges::join_with_view<Range, Pattern> view(range, pattern);
     std::same_as<Range> decltype(auto) result = view.base();
@@ -88,8 +86,7 @@ constexpr bool test() {
     assert(result.end() == buff + 3);
   }
 
-  // Check the && overload
-  {
+  { // Check the && overload
     Range range(buff, buff + 3);
     std::ranges::join_with_view<Range, Pattern> view(range, pattern);
     std::same_as<Range> decltype(auto) result = std::move(view).base();
@@ -98,18 +95,7 @@ constexpr bool test() {
     assert(result.end() == buff + 3);
   }
 
-  // Check the && overload on const `view`
-  {
-    Range range(buff, buff + 3);
-    const std::ranges::join_with_view<Range, Pattern> view(range, pattern);
-    std::same_as<Range> decltype(auto) result = std::move(view).base();
-    assert(result.was_copy_initialized_);
-    assert(result.begin() == buff);
-    assert(result.end() == buff + 3);
-  }
-
-  // Ensure the const& overload is not considered when the base is not copy-constructible
-  {
+  { // Ensure the const& overload is not considered when the base is not copy-constructible
     static_assert(!CanCallBaseOn<const std::ranges::join_with_view<NonCopyableRange<InnerRange>, Pattern>&>);
     static_assert(!CanCallBaseOn<std::ranges::join_with_view<NonCopyableRange<InnerRange>, Pattern>&>);
     static_assert(!CanCallBaseOn<const std::ranges::join_with_view<NonCopyableRange<InnerRange>, Pattern>&&>);
@@ -117,17 +103,15 @@ constexpr bool test() {
     static_assert(CanCallBaseOn<std::ranges::join_with_view<NonCopyableRange<InnerRange>, Pattern>>);
   }
 
-  // Ensure the const& overload does not depend on Pattern's copy-constructability
-  {
-    static_assert(CanCallBaseOn<const std::ranges::join_with_view<Range, NonCopyableRange<int>>& >);
-    static_assert(CanCallBaseOn<std::ranges::join_with_view<Range, NonCopyableRange<int>>& >);
-    static_assert(CanCallBaseOn<const std::ranges::join_with_view<Range, NonCopyableRange<int>>&& >);
-    static_assert(CanCallBaseOn<std::ranges::join_with_view<Range, NonCopyableRange<int>>&& >);
+  { // Ensure the const& overload does not depend on Pattern's copy-constructability
+    static_assert(CanCallBaseOn<const std::ranges::join_with_view<Range, NonCopyableRange<int>>&>);
+    static_assert(CanCallBaseOn<std::ranges::join_with_view<Range, NonCopyableRange<int>>&>);
+    static_assert(CanCallBaseOn<const std::ranges::join_with_view<Range, NonCopyableRange<int>>&&>);
+    static_assert(CanCallBaseOn<std::ranges::join_with_view<Range, NonCopyableRange<int>>&&>);
     static_assert(CanCallBaseOn<std::ranges::join_with_view<Range, NonCopyableRange<int>>>);
   }
 
-  // Check above two at the same time
-  {
+  { // Check above two at the same time
     static_assert(
         !CanCallBaseOn<const std::ranges::join_with_view<NonCopyableRange<InnerRange>, NonCopyableRange<int>>&>);
     static_assert(!CanCallBaseOn<std::ranges::join_with_view<NonCopyableRange<InnerRange>, NonCopyableRange<int>>&>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/begin.pass.cpp
index 70004394d28816..849cad84d6b027 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/begin.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/begin.pass.cpp
@@ -10,24 +10,12 @@
 
 // <ranges>
 
-// constexpr auto begin() {
-//   if constexpr (forward_range<V>) {
-//     constexpr bool use_const =
-//       simple-view<V> && is_reference_v<InnerRng> && simple-view<Pattern>;
-//     return iterator<use_const>{*this, ranges::begin(base_)};
-//   }
-//   else {
-//     outer_it_ = ranges::begin(base_);
-//     return iterator<false>{*this};
-//   }
-// }
+// constexpr auto begin();
 // constexpr auto begin() const
 //   requires forward_range<const V> &&
 //            forward_range<const Pattern> &&
 //            is_reference_v<range_reference_t<const V>> &&
-//            input_range<range_reference_t<const V>> {
-//   return iterator<true>{*this, ranges::begin(base_)};
-// }
+//            input_range<range_reference_t<const V>>;
 
 #include <ranges>
 
@@ -49,30 +37,6 @@ using MaybeSimpleForwardRvalueView =
 template <bool Simple>
 using MaybeSimplePattern = BasicView<std::string, ViewProperties{.simple = Simple}, forward_iterator>;
 
-class DifferentWhenConstV {
-  using Arr                      = std::array<std::array<int, 2>, 2>;
-  static constexpr Arr regular_v = {{{1, 2}, {3, 4}}};
-  static constexpr Arr const_v   = {{{5, 6}, {7, 8}}};
-
-public:
-  constexpr auto begin() { return regular_v.begin(); }
-  constexpr auto end() { return regular_v.end(); }
-  constexpr auto begin() const { return const_v.begin(); }
-  constexpr auto end() const { return const_v.end(); }
-};
-
-class DifferentWhenConstPattern {
-  using Pat                            = std::array<int, 2>;
-  static constexpr Pat regular_pattern = {0, 0};
-  static constexpr Pat const_pattern   = {1, 1};
-
-public:
-  constexpr auto begin() { return regular_pattern.begin(); }
-  constexpr auto end() { return regular_pattern.end(); }
-  constexpr auto begin() const { return const_pattern.begin(); }
-  constexpr auto end() const { return const_pattern.end(); }
-};
-
 template <class V, class Pattern>
 concept JoinWithViewHasConstBegin = requires(const std::ranges::join_with_view<V, Pattern> jwv) {
   { jwv.begin() } -> std::input_iterator;
@@ -187,64 +151,59 @@ constexpr void test_begin() {
 }
 
 constexpr void test_const_begin() {
-  { // `const V` models forward range
-    // `const Pattern` models forward range
-    // `is_reference_v<range_reference_t<const V>>` is true
-    // `range_reference_t<const V>` models input range
-    using V       = BasicView<DifferentWhenConstV, ViewProperties{}, forward_iterator>;
-    using Pattern = BasicView<DifferentWhenConstPattern, ViewProperties{}, forward_iterator>;
-
-    const std::ranges::join_with_view<V, Pattern> jwv;
-    auto it = jwv.begin();
-    assert(std::ranges::equal(std::views::counted(it, 6), std::array{5, 6, 1, 1, 7, 8}));
-  }
-
   using Vec = std::vector<std::array<int, 2>>;
   using Pat = std::array<int, 2>;
 
-  { // `const V` does not model forward range
-    // `const Pattern` models forward range
-    // `is_reference_v<range_reference_t<const V>>` is true
-    // `range_reference_t<const V>` models input range
-    static_assert(!JoinWithViewHasConstBegin<BasicView<Vec, ViewProperties{.common = false}, cpp20_input_iterator>,
-                                             BasicView<Pat, ViewProperties{}, forward_iterator>>);
-  }
-
-  { // `const V` models forward range
-    // `const Pattern` does not model forward range
-    // `is_reference_v<range_reference_t<const V>>` is true
-    // `range_reference_t<const V>` models input range
-    static_assert(!JoinWithViewHasConstBegin<BasicView<Vec, ViewProperties{}, forward_iterator>,
-                                             BasicView<Pat, ViewProperties{.common = false}, cpp20_input_iterator>>);
-  }
-
-  { // `const V` models forward range
-    // `const Pattern` models forward range
-    // `is_reference_v<range_reference_t<const V>>` is false
-    // `range_reference_t<const V>` models input range
-    static_assert(
-        !JoinWithViewHasConstBegin<BasicView<RvalueVector<std::vector<int>>, ViewProperties{}, forward_iterator>,
-                                   BasicView<Pat, ViewProperties{}, forward_iterator>>);
-  }
-
   { // `const V` models forward range
     // `const Pattern` models forward range
     // `is_reference_v<range_reference_t<const V>>` is true
-    // `range_reference_t<const V>` does not model input range
-    static_assert(!JoinWithViewHasConstBegin<
-                  BasicView<std::vector<InputRangeButOutputWhenConst<int>>, ViewProperties{}, forward_iterator>,
-                  BasicView<Pat, ViewProperties{}, forward_iterator>>);
-  }
-
-  { // Check situation when iterators returned by `begin()` and `begin() const` are the same
-    using JWV = std::ranges::join_with_view<MaybeSimpleForwardView<true>, MaybeSimplePattern<true>>;
-    static_assert(std::same_as<std::ranges::iterator_t<JWV&>, std::ranges::iterator_t<const JWV&>>);
-  }
+    // `range_reference_t<const V>` models input range
+    using V       = BasicView<Vec, ViewProperties{}, forward_iterator>;
+    using Pattern = BasicView<Pat, ViewProperties{}, forward_iterator>;
 
-  { // Check LWG-4074: compatible-joinable-ranges is underconstrained
-    static_assert(!JoinWithViewHasConstBegin<BasicVectorView<int, ViewProperties{}, forward_iterator>,
-                                             lwg4074::PatternWithProxyConstAccess>);
-  }
+    const std::ranges::join_with_view<V, Pattern> jwv{V{Vec{std::array{1, 2}, std::array{3, 4}}}, Pattern{Pat{0, 0}}};
+    auto it = jwv.begin();
+    assert(std::ranges::equal(std::views::counted(it, 6), std::array{1, 2, 0, 0, 3, 4}));
+  }
+
+  // `const V` does not model forward range
+  // `const Pattern` models forward range
+  // `is_reference_v<range_reference_t<const V>>` is true
+  // `range_reference_t<const V>` models input range
+  static_assert(!JoinWithViewHasConstBegin<BasicView<Vec, ViewProperties{.common = false}, cpp20_input_iterator>,
+                                           BasicView<Pat, ViewProperties{}, forward_iterator>>);
+
+  // `const V` models forward range
+  // `const Pattern` does not model forward range
+  // `is_reference_v<range_reference_t<const V>>` is true
+  // `range_reference_t<const V>` models input range
+  static_assert(!JoinWithViewHasConstBegin<BasicView<Vec, ViewProperties{}, forward_iterator>,
+                                           BasicView<Pat, ViewProperties{.common = false}, cpp20_input_iterator>>);
+
+  // `const V` models forward range
+  // `const Pattern` models forward range
+  // `is_reference_v<range_reference_t<const V>>` is false
+  // `range_reference_t<const V>` models input range
+  static_assert(
+      !JoinWithViewHasConstBegin<BasicView<RvalueVector<std::vector<int>>, ViewProperties{}, forward_iterator>,
+                                 BasicView<Pat, ViewProperties{}, forward_iterator>>);
+
+  // `const V` models forward range
+  // `const Pattern` models forward range
+  // `is_reference_v<range_reference_t<const V>>` is true
+  // `range_reference_t<const V>` does not model input range
+  static_assert(!JoinWithViewHasConstBegin<
+                BasicView<std::vector<InputRangeButOutputWhenConst<int>>, ViewProperties{}, forward_iterator>,
+                BasicView<Pat, ViewProperties{}, forward_iterator>>);
+
+  // `concatable<range_reference_t<const V>, const Pattern>` is not satisfied
+  // See also LWG-4074: compatible-joinable-ranges is underconstrained
+  static_assert(!JoinWithViewHasConstBegin<BasicVectorView<int, ViewProperties{}, forward_iterator>,
+                                           lwg4074::PatternWithProxyConstAccess>);
+
+  // Check situation when iterators returned by `begin()` and `begin() const` are the same
+  using JWV = std::ranges::join_with_view<MaybeSimpleForwardView<true>, MaybeSimplePattern<true>>;
+  static_assert(std::same_as<std::ranges::iterator_t<JWV&>, std::ranges::iterator_t<const JWV&>>);
 }
 
 constexpr bool test() {
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/end.pass.cpp
index 6334b2c9a96d15..b73ee1f54d6cc8 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/end.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/end.pass.cpp
@@ -10,25 +10,11 @@
 
 // <ranges>
 
-// constexpr auto end() {
-//   if constexpr (forward_range<V> &&
-//                 is_reference_v<InnerRng> && forward_range<InnerRng> &&
-//                 common_range<V> && common_range<InnerRng>)
-//     return iterator<simple-view<V> && simple-view<Pattern>>{*this, ranges::end(base_)};
-//   else
-//     return sentinel<simple-view<V> && simple-view<Pattern>>{*this};
-// }
+// constexpr auto end();
 // constexpr auto end() const
 //   requires forward_range<const V> && forward_range<const Pattern> &&
 //            is_reference_v<range_reference_t<const V>> &&
-//            input_range<range_reference_t<const V>> {
-//   using InnerConstRng = range_reference_t<const V>;
-//   if constexpr (forward_range<InnerConstRng> &&
-//                 common_range<const V> && common_range<InnerConstRng>)
-//     return iterator<true>{*this, ranges::end(base_)};
-//   else
-//     return sentinel<true>{*this};
-// }
+//            input_range<range_reference_t<const V>>;
 
 // ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=10000000
 

>From ee942ceeeca4a678c6ec9fad448055acff3ac352 Mon Sep 17 00:00:00 2001
From: Jakub Mazurkiewicz <mazkuba3 at gmail.com>
Date: Mon, 16 Dec 2024 21:23:18 +0100
Subject: [PATCH 11/15] Fix formatting

---
 libcxx/test/std/ranges/range.adaptors/range.join.with/types.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/types.h b/libcxx/test/std/ranges/range.adaptors/range.join.with/types.h
index 588c647cef23a7..d3ffbccd5056fb 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.with/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/types.h
@@ -37,7 +37,8 @@ struct ViewProperties {
 template <std::ranges::input_range Data,
           ViewProperties Prop,
           template <class...> class It,
-          template <class...> class ConstIt = It>
+          template <class...>
+          class ConstIt = It>
 class BasicView : public std::ranges::view_base {
   Data data_;
 

>From 56d885083ddcaeba9315b2cf243cc562c9b303c3 Mon Sep 17 00:00:00 2001
From: Jakub Mazurkiewicz <mazkuba3 at gmail.com>
Date: Mon, 16 Dec 2024 21:26:29 +0100
Subject: [PATCH 12/15] Fix `example.pass.cpp` test

---
 .../range.join.with/range.join.with.overview/example.pass.cpp  | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.overview/example.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.overview/example.pass.cpp
index a4b05e598196bd..8b55b0a9deaaa5 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.overview/example.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.overview/example.pass.cpp
@@ -20,6 +20,7 @@
 
 #include <ranges>
 
+#include <cassert>
 #include <string>
 #include <vector>
 
@@ -34,7 +35,7 @@ constexpr bool test() {
 }
 
 int main(int, char**) {
-  test();
+  assert(test());
   static_assert(test());
 
   return 0;

>From 15efa6f0765064408f432df8de8e4ae6ef5dcd3d Mon Sep 17 00:00:00 2001
From: Jakub Mazurkiewicz <mazkuba3 at gmail.com>
Date: Mon, 16 Dec 2024 22:16:13 +0100
Subject: [PATCH 13/15] Fix `19.rst` file

---
 libcxx/docs/ReleaseNotes/19.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/19.rst b/libcxx/docs/ReleaseNotes/19.rst
index 3e1b7e89d36573..e8f76773c54373 100644
--- a/libcxx/docs/ReleaseNotes/19.rst
+++ b/libcxx/docs/ReleaseNotes/19.rst
@@ -78,7 +78,7 @@ Implemented Papers
 - P1981R0 - Rename ``leap`` to ``leap_second``
 - P1982R0 - Rename ``link`` to ``time_zone_link``
 - P2602R2 - Poison Pills are Too Toxic (as DR against C++20)
-- 
+
 
 Improvements and New Features
 -----------------------------

>From f267d7644496b7ece7dcd3963b94eeb0ec8ce3b4 Mon Sep 17 00:00:00 2001
From: Jakub Mazurkiewicz <mazkuba3 at gmail.com>
Date: Mon, 16 Dec 2024 23:21:54 +0100
Subject: [PATCH 14/15] Enable `iterator/no_unique_address.compile.pass.cpp`
 test for MSVC

---
 .../range.join.with.iterator/no_unique_address.compile.pass.cpp  | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/no_unique_address.compile.pass.cpp
index ba1d233f5e33e3..088684314b5dde 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/no_unique_address.compile.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/no_unique_address.compile.pass.cpp
@@ -7,7 +7,6 @@
 //===----------------------------------------------------------------------===//
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-// XFAIL: msvc
 
 // <ranges>
 

>From 3ad462cc9f2428fecb359da7bc12315aa3942769 Mon Sep 17 00:00:00 2001
From: Jakub Mazurkiewicz <mazkuba3 at gmail.com>
Date: Tue, 17 Dec 2024 00:57:38 +0100
Subject: [PATCH 15/15] Fix formatting (again)

---
 libcxx/test/std/ranges/range.adaptors/range.join.with/types.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/types.h b/libcxx/test/std/ranges/range.adaptors/range.join.with/types.h
index d3ffbccd5056fb..0d2830a45b74f7 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.with/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/types.h
@@ -36,9 +36,9 @@ struct ViewProperties {
 
 template <std::ranges::input_range Data,
           ViewProperties Prop,
-          template <class...> class It,
           template <class...>
-          class ConstIt = It>
+          class It,
+          template <class...> class ConstIt = It>
 class BasicView : public std::ranges::view_base {
   Data data_;
 



More information about the libcxx-commits mailing list