[libcxx-commits] [libcxx] 866945c - [libc++][ranges] P2542R8: Implement `views::concat` (#120920)

via libcxx-commits libcxx-commits at lists.llvm.org
Sat May 30 15:26:22 PDT 2026


Author: Nhat Nguyen
Date: 2026-05-30T23:26:16+01:00
New Revision: 866945c20298c1bca4af6973a31f4739dfe49e91

URL: https://github.com/llvm/llvm-project/commit/866945c20298c1bca4af6973a31f4739dfe49e91
DIFF: https://github.com/llvm/llvm-project/commit/866945c20298c1bca4af6973a31f4739dfe49e91.diff

LOG: [libc++][ranges] P2542R8: Implement `views::concat` (#120920)

Closes #105419
Closes #105348
Closes #105349
Closes #171314

Assisted-by: Chatgpt. 
I use AI to help me write some tests, however. I have reviewed the code
I submit in the tests.

---------

Co-authored-by: A. Jiang <de34 at live.cn>
Co-authored-by: Hristo Hristov <hghristov.rmm at gmail.com>
Co-authored-by: Hristo Hristov <zingam at outlook.com>

Added: 
    libcxx/include/__ranges/concat_view.h
    libcxx/test/libcxx/ranges/range.adaptors/range.concat/iterator.valueless_by_exception.pass.cpp
    libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
    libcxx/test/std/ranges/range.adaptors/range.concat/adaptor.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.concat/begin.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.concat/constraints.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.concat/ctad.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.concat/ctor.default.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.concat/ctor.views.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.concat/end.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.concat/iterator/arithmetic.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.concat/iterator/compare.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.concat/iterator/ctor.default.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.concat/iterator/ctor.other.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.concat/iterator/decrement.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.concat/iterator/deref.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.concat/iterator/increment.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.concat/iterator/iter_move.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.concat/iterator/iter_swap.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.concat/iterator/member_types.compile.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.concat/iterator/subscript.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.concat/size.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.concat/types.h

Modified: 
    libcxx/docs/FeatureTestMacroTable.rst
    libcxx/docs/ReleaseNotes/23.rst
    libcxx/docs/Status/Cxx2cIssues.csv
    libcxx/docs/Status/Cxx2cPapers.csv
    libcxx/include/CMakeLists.txt
    libcxx/include/module.modulemap.in
    libcxx/include/ranges
    libcxx/include/version
    libcxx/modules/std/ranges.inc
    libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
    libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
    libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
    libcxx/utils/generate_feature_test_macro_components.py

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index ae48eaed1f46b..4eb15fa0eb131 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -506,7 +506,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_philox_engine``                                *unimplemented*
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_ranges_concat``                                *unimplemented*
+    ``__cpp_lib_ranges_concat``                                ``202403L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ranges_indices``                               ``202506L``
     ---------------------------------------------------------- -----------------

diff  --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 843f25dc7708a..e94897ffaf437 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -48,6 +48,7 @@ Implemented Papers
 - P2164R9: ``views::enumerate`` (`Github <https://llvm.org/PR105251>`__)
 - P2322R6: ``ranges::fold`` (`Github <https://llvm.org/PR105208>`__)
 - P4144R1: Remove ``span``'s ``initializer_list`` constructor for C++26 (`Github <https://llvm.org/PR189612>`__)
+- P2542R8: ``views::concat`` (`Github <https://llvm.org/PR105419>`__)
 - P3383R3: ``mdspan.at()`` (`Github <https://llvm.org/PR175213>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
 

diff  --git a/libcxx/docs/Status/Cxx2cIssues.csv b/libcxx/docs/Status/Cxx2cIssues.csv
index e329e14f70ab3..20b0b93eff753 100644
--- a/libcxx/docs/Status/Cxx2cIssues.csv
+++ b/libcxx/docs/Status/Cxx2cIssues.csv
@@ -68,8 +68,8 @@
 "`LWG4071 <https://wg21.link/LWG4071>`__","``reference_wrapper`` comparisons are not SFINAE-friendly","2024-06 (St. Louis)","|Complete|","19","`#105345 <https://github.com/llvm/llvm-project/issues/105345>`__",""
 "`LWG4074 <https://wg21.link/LWG4074>`__","``compatible-joinable-ranges`` is underconstrained","2024-06 (St. Louis)","|Complete|","21","`#105346 <https://github.com/llvm/llvm-project/issues/105346>`__",""
 "`LWG4076 <https://wg21.link/LWG4076>`__","``concat_view`` should be freestanding","2024-06 (St. Louis)","","","`#105347 <https://github.com/llvm/llvm-project/issues/105347>`__",""
-"`LWG4079 <https://wg21.link/LWG4079>`__","Missing Preconditions in ``concat_view::iterator``\`s conversion constructor","2024-06 (St. Louis)","","","`#105348 <https://github.com/llvm/llvm-project/issues/105348>`__",""
-"`LWG4082 <https://wg21.link/LWG4082>`__","``views::concat(r)`` is well-formed when ``r`` is an ``output_range``","2024-06 (St. Louis)","","","`#105349 <https://github.com/llvm/llvm-project/issues/105349>`__",""
+"`LWG4079 <https://wg21.link/LWG4079>`__","Missing Preconditions in ``concat_view::iterator``\`s conversion constructor","2024-06 (St. Louis)","|Complete|","23","`#105348 <https://github.com/llvm/llvm-project/issues/105348>`__",""
+"`LWG4082 <https://wg21.link/LWG4082>`__","``views::concat(r)`` is well-formed when ``r`` is an ``output_range``","2024-06 (St. Louis)","|Complete|","23","`#105349 <https://github.com/llvm/llvm-project/issues/105349>`__",""
 "`LWG4083 <https://wg21.link/LWG4083>`__","``views::as_rvalue`` should reject non-input ranges","2024-06 (St. Louis)","|Complete|","22","`#105351 <https://github.com/llvm/llvm-project/issues/105351>`__",""
 "`LWG4096 <https://wg21.link/LWG4096>`__","``views::iota(views::iota(0))`` should be rejected","2024-06 (St. Louis)","|Complete|","22","`#105352 <https://github.com/llvm/llvm-project/issues/105352>`__",""
 "`LWG4098 <https://wg21.link/LWG4098>`__","``views::adjacent<0>`` should reject non-forward ranges","2024-06 (St. Louis)","","","`#105353 <https://github.com/llvm/llvm-project/issues/105353>`__",""
@@ -155,7 +155,7 @@
 "`LWG4020 <https://wg21.link/LWG4020>`__","``extents::index-cast`` weirdness","2025-11 (Kona)","|Complete|","23","`#171311 <https://github.com/llvm/llvm-project/issues/171311>`__",""
 "`LWG4136 <https://wg21.link/LWG4136>`__","Specify behavior of [linalg] Hermitian algorithms on diagonal with nonzero imaginary part","2025-11 (Kona)","","","`#171312 <https://github.com/llvm/llvm-project/issues/171312>`__",""
 "`LWG4137 <https://wg21.link/LWG4137>`__","Fix *Mandates*, *Preconditions*, and *Complexity* elements of [linalg] algorithms","2025-11 (Kona)","","","`#171313 <https://github.com/llvm/llvm-project/issues/171313>`__",""
-"`LWG4166 <https://wg21.link/LWG4166>`__","``concat_view::end()`` should be more constrained in order to support noncopyable iterators","2025-11 (Kona)","","","`#171314 <https://github.com/llvm/llvm-project/issues/171314>`__",""
+"`LWG4166 <https://wg21.link/LWG4166>`__","``concat_view::end()`` should be more constrained in order to support noncopyable iterators","2025-11 (Kona)","|Complete|","23","`#171314 <https://github.com/llvm/llvm-project/issues/171314>`__",""
 "`LWG4230 <https://wg21.link/LWG4230>`__","``simd<complex>::real/imag`` is overconstrained","2025-11 (Kona)","","","`#171316 <https://github.com/llvm/llvm-project/issues/171316>`__",""
 "`LWG4243 <https://wg21.link/LWG4243>`__","``as_bytes``/``as_writable_bytes`` is broken with ``span<volatile T>``","2025-11 (Kona)","","","`#171317 <https://github.com/llvm/llvm-project/issues/171317>`__",""
 "`LWG4251 <https://wg21.link/LWG4251>`__","Move assignment for ``indirect`` unnecessarily requires copy construction","2025-11 (Kona)","","","`#171318 <https://github.com/llvm/llvm-project/issues/171318>`__",""

diff  --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index 5b229c75cc8c0..2132e80251657 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -54,7 +54,7 @@
 "`P3142R0 <https://wg21.link/P3142R0>`__","Printing Blank Lines with ``println``","2024-03 (Tokyo)","|Complete|","19","`#105415 <https://github.com/llvm/llvm-project/issues/105415>`__","Implemented as a DR against C++23. (MSVC STL and libstdc++ will do the same.)"
 "`P2845R8 <https://wg21.link/P2845R8>`__","Formatting of ``std::filesystem::path``","2024-03 (Tokyo)","","","`#105416 <https://github.com/llvm/llvm-project/issues/105416>`__",""
 "`P0493R5 <https://wg21.link/P0493R5>`__","Atomic minimum/maximum","2024-03 (Tokyo)","","","`#105418 <https://github.com/llvm/llvm-project/issues/105418>`__",""
-"`P2542R8 <https://wg21.link/P2542R8>`__","``views::concat``","2024-03 (Tokyo)","","","`#105419 <https://github.com/llvm/llvm-project/issues/105419>`__",""
+"`P2542R8 <https://wg21.link/P2542R8>`__","``views::concat``","2024-03 (Tokyo)","|Complete|","23","`#105419 <https://github.com/llvm/llvm-project/issues/105419>`__",""
 "`P2591R5 <https://wg21.link/P2591R5>`__","Concatenation of strings and string views","2024-03 (Tokyo)","|Complete|","19","`#105420 <https://github.com/llvm/llvm-project/issues/105420>`__",""
 "`P2248R8 <https://wg21.link/P2248R8>`__","Enabling list-initialization for algorithms","2024-03 (Tokyo)","","","`#105421 <https://github.com/llvm/llvm-project/issues/105421>`__",""
 "`P2810R4 <https://wg21.link/P2810R4>`__","``is_debugger_present`` ``is_replaceable``","2024-03 (Tokyo)","","","`#105422 <https://github.com/llvm/llvm-project/issues/105422>`__",""

diff  --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 5e9040a62dd53..10dfb4b4d58cb 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -724,6 +724,7 @@ set(files
   __ranges/as_rvalue_view.h
   __ranges/chunk_by_view.h
   __ranges/common_view.h
+  __ranges/concat_view.h
   __ranges/concepts.h
   __ranges/container_compatible_range.h
   __ranges/counted.h

diff  --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h
new file mode 100644
index 0000000000000..3bbe9db12e0f8
--- /dev/null
+++ b/libcxx/include/__ranges/concat_view.h
@@ -0,0 +1,651 @@
+// -*- 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_CONCAT_VIEW_H
+#define _LIBCPP___RANGES_CONCAT_VIEW_H
+
+#include <__assert>
+#include <__concepts/common_reference_with.h>
+#include <__concepts/constructible.h>
+#include <__concepts/convertible_to.h>
+#include <__concepts/copyable.h>
+#include <__concepts/derived_from.h>
+#include <__concepts/equality_comparable.h>
+#include <__concepts/swappable.h>
+#include <__config>
+#include <__iterator/concepts.h>
+#include <__iterator/default_sentinel.h>
+#include <__iterator/distance.h>
+#include <__iterator/incrementable_traits.h>
+#include <__iterator/iter_move.h>
+#include <__iterator/iter_swap.h>
+#include <__iterator/iterator_traits.h>
+#include <__iterator/next.h>
+#include <__ranges/access.h>
+#include <__ranges/all.h>
+#include <__ranges/concepts.h>
+#include <__ranges/movable_box.h>
+#include <__ranges/range_adaptor.h>
+#include <__ranges/size.h>
+#include <__ranges/view_interface.h>
+#include <__tuple/tuple_transform.h>
+#include <__type_traits/conditional.h>
+#include <__type_traits/decay.h>
+#include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/make_unsigned.h>
+#include <__type_traits/maybe_const.h>
+#include <__utility/forward.h>
+#include <__utility/in_place.h>
+#include <__utility/move.h>
+#include <tuple>
+#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 >= 26
+
+namespace ranges {
+
+#  ifdef __cpp_pack_indexing
+template <class... _Tp>
+using __extract_last _LIBCPP_NODEBUG = _Tp...[sizeof...(_Tp) - 1];
+#  else
+template <class _Tp, class... _Tail>
+struct __extract_last_impl : __extract_last_impl<_Tail...> {};
+template <class _Tp>
+struct __extract_last_impl<_Tp> {
+  using type _LIBCPP_NODEBUG = _Tp;
+};
+
+template <class... _Tp>
+using __extract_last _LIBCPP_NODEBUG = __extract_last_impl<_Tp...>::type;
+#  endif
+
+template <bool _Const, class... _Tp>
+struct __all_but_first_model_sized_range;
+
+template <bool _Const, class _Head, class... _Tail>
+struct __all_but_first_model_sized_range<_Const, _Head, _Tail...> {
+  static constexpr bool value = (sized_range<__maybe_const<_Const, _Tail>> && ...);
+};
+
+template <bool _Const, class... _Views>
+concept __all_random_access = (random_access_range<__maybe_const<_Const, _Views>> && ...);
+
+template <bool _Const, class... _Views>
+concept __all_bidirectional = (bidirectional_range<__maybe_const<_Const, _Views>> && ...);
+
+template <bool _Const, class... _Views>
+concept __all_forward = (forward_range<__maybe_const<_Const, _Views>> && ...);
+
+template <bool _Const, class _First, class... _Tail>
+struct __all_common_ignore_last {
+  static constexpr bool value =
+      common_range<__maybe_const<_Const, _First>> && __all_common_ignore_last<_Const, _Tail...>::value;
+};
+
+template <bool _Const, class _Tail>
+struct __all_common_ignore_last<_Const, _Tail> {
+  static constexpr bool value = true;
+};
+
+template <bool _Const, class... _Rs>
+concept __concat_is_random_access =
+    (__all_random_access<_Const, _Rs...>) && (__all_common_ignore_last<_Const, _Rs...>::value);
+
+template <bool _Const, class... _Rs>
+concept __concat_is_bidirectional =
+    (__all_bidirectional<_Const, _Rs...>) && (__all_common_ignore_last<_Const, _Rs...>::value);
+
+template <input_range... _Views>
+  requires((view<_Views> && ...) && (sizeof...(_Views) > 0) && __concatable<_Views...>)
+class concat_view : public view_interface<concat_view<_Views...>> {
+  tuple<_Views...> __views_;
+
+  template <bool _Const>
+  class __iterator;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI constexpr concat_view() = default;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit concat_view(_Views... __views) : __views_(std::move(__views)...) {}
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __iterator<false> begin()
+    requires(!(__simple_view<_Views> && ...))
+  {
+    __iterator<false> __it(this, in_place_index<0>, ranges::begin(std::get<0>(__views_)));
+    __it.template __satisfy<0>();
+    return __it;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __iterator<true> begin() const
+    requires((range<const _Views> && ...) && __concatable<const _Views...>)
+  {
+    __iterator<true> __it(this, in_place_index<0>, ranges::begin(std::get<0>(__views_)));
+    __it.template __satisfy<0>();
+    return __it;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+    requires(!(__simple_view<_Views> && ...))
+  {
+    if constexpr (__all_forward<false, _Views...> && common_range<__maybe_const<false, __extract_last<_Views...>>>) {
+      constexpr auto __n = sizeof...(_Views);
+      return __iterator<false>(this, in_place_index<__n - 1>, ranges::end(std::get<__n - 1>(__views_)));
+    } else {
+      return default_sentinel;
+    }
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+    requires((range<const _Views> && ...) && __concatable<const _Views...>)
+  {
+    if constexpr (__all_forward<true, _Views...> && common_range<__maybe_const<true, __extract_last<_Views...>>>) {
+      constexpr auto __n = sizeof...(_Views);
+      return __iterator<true>(this, in_place_index<__n - 1>, ranges::end(std::get<__n - 1>(__views_)));
+    } else {
+      return default_sentinel;
+    }
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+    requires(sized_range<_Views> && ...)
+  {
+    return std::apply(
+        [](auto... __sizes) { return (make_unsigned_t<common_type_t<decltype(__sizes)...>>(__sizes) + ...); },
+        std::__tuple_transform(ranges::size, __views_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+    requires(sized_range<const _Views> && ...)
+  {
+    return std::apply(
+        [](auto... __sizes) { return (make_unsigned_t<common_type_t<decltype(__sizes)...>>(__sizes) + ...); },
+        std::__tuple_transform(ranges::size, __views_));
+  }
+};
+
+template <class... _Views>
+concat_view(_Views&&...) -> concat_view<views::all_t<_Views>...>;
+
+template <bool _Const, typename... _Views>
+struct __concat_view_iterator_category {};
+
+template <bool _Const, typename... _Views>
+  requires __all_forward<_Const, _Views...>
+struct __concat_view_iterator_category<_Const, _Views...> {
+private:
+  constexpr static bool __derive_pack_random_iterator =
+      (derived_from<typename iterator_traits<iterator_t<__maybe_const<_Const, _Views>>>::iterator_category,
+                    random_access_iterator_tag> &&
+       ...);
+  constexpr static bool __derive_pack_bidirectional_iterator =
+      (derived_from<typename iterator_traits<iterator_t<__maybe_const<_Const, _Views>>>::iterator_category,
+                    bidirectional_iterator_tag> &&
+       ...);
+  constexpr static bool __derive_pack_forward_iterator =
+      (derived_from<typename iterator_traits< iterator_t<__maybe_const<_Const, _Views>>>::iterator_category,
+                    forward_iterator_tag> &&
+       ...);
+
+public:
+  using iterator_category =
+      _If<!is_reference_v<__concat_reference_t<__maybe_const<_Const, _Views>...>>,
+          input_iterator_tag,
+          _If<__derive_pack_random_iterator,
+              random_access_iterator_tag,
+              _If<__derive_pack_bidirectional_iterator,
+                  bidirectional_iterator_tag,
+                  _If<__derive_pack_forward_iterator, forward_iterator_tag, input_iterator_tag > > > >;
+};
+
+template <input_range... _Views>
+  requires((view<_Views> && ...) && (sizeof...(_Views) > 0) && __concatable<_Views...>)
+template <bool _Const>
+class concat_view<_Views...>::__iterator : public __concat_view_iterator_category<_Const, _Views...> {
+public:
+  using iterator_concept =
+      _If<__concat_is_random_access<_Const, _Views...>,
+          random_access_iterator_tag,
+          _If<__concat_is_bidirectional<_Const, _Views...>,
+              bidirectional_iterator_tag,
+              _If< __all_forward<_Const, _Views...>, forward_iterator_tag, input_iterator_tag > > >;
+  using value_type      = __concat_value_t<__maybe_const<_Const, _Views>...>;
+  using 
diff erence_type = common_type_t<range_
diff erence_t<__maybe_const<_Const, _Views>>...>;
+
+private:
+  using __base_iter _LIBCPP_NODEBUG = variant<iterator_t<__maybe_const<_Const, _Views>>...>;
+  __base_iter __it_;
+  __maybe_const<_Const, concat_view>* __parent_ = nullptr;
+
+  template <size_t _Idx>
+  _LIBCPP_HIDE_FROM_ABI constexpr void __satisfy() {
+    if constexpr (_Idx < (sizeof...(_Views) - 1)) {
+      if (std::get<_Idx>(__it_) == ranges::end(std::get<_Idx>(__parent_->__views_))) {
+        __it_.template emplace<_Idx + 1>(ranges::begin(std::get<_Idx + 1>(__parent_->__views_)));
+        __satisfy<_Idx + 1>();
+      }
+    }
+  }
+
+  template <size_t _Idx>
+  _LIBCPP_HIDE_FROM_ABI constexpr void __prev() {
+    if constexpr (_Idx == 0) {
+      --std::get<0>(__it_);
+    } else {
+      if (std::get<_Idx>(__it_) == ranges::begin(std::get<_Idx>(__parent_->__views_))) {
+        __it_.template emplace<_Idx - 1>(ranges::end(std::get<_Idx - 1>(__parent_->__views_)));
+        __prev<_Idx - 1>();
+      } else {
+        --std::get<_Idx>(__it_);
+      }
+    }
+  }
+
+  template <size_t _Idx>
+  _LIBCPP_HIDE_FROM_ABI constexpr void __advance_fwd(
diff erence_type __offset, 
diff erence_type __steps) {
+    using __underlying_
diff _type = iter_
diff erence_t<variant_alternative_t<_Idx, __base_iter>>;
+    if constexpr (_Idx == sizeof...(_Views) - 1) {
+      std::get<_Idx>(__it_) += static_cast<__underlying_
diff _type>(__steps);
+    } else {
+      auto __n_size = ranges::distance(std::get<_Idx>(__parent_->__views_));
+      if (__offset + __steps < __n_size) {
+        std::get<_Idx>(__it_) += static_cast<__underlying_
diff _type>(__steps);
+      } else {
+        __it_.template emplace<_Idx + 1>(ranges::begin(std::get<_Idx + 1>(__parent_->__views_)));
+        __advance_fwd<_Idx + 1>(0, __offset + __steps - __n_size);
+      }
+    }
+  }
+
+  template <size_t _Idx>
+  _LIBCPP_HIDE_FROM_ABI constexpr void __advance_bwd(
diff erence_type __offset, 
diff erence_type __steps) {
+    using __underlying_
diff _type = iter_
diff erence_t<variant_alternative_t<_Idx, __base_iter>>;
+    if constexpr (_Idx == 0) {
+      std::get<_Idx>(__it_) -= static_cast<__underlying_
diff _type>(__steps);
+    } else {
+      if (__offset >= __steps) {
+        std::get<_Idx>(__it_) -= static_cast<__underlying_
diff _type>(__steps);
+      } else {
+        auto __prev_size = ranges::distance(std::get<_Idx - 1>(__parent_->__views_));
+        __it_.template emplace<_Idx - 1>(ranges::end(std::get<_Idx - 1>(__parent_->__views_)));
+        __advance_bwd<_Idx - 1>(__prev_size, __steps - __offset);
+      }
+    }
+  }
+
+  template <typename _Func>
+  _LIBCPP_HIDE_FROM_ABI constexpr auto __invoke_at_index(_Func&& __func) const {
+    // TODO(GCC 16): Just capture `this` when GCC PR113563 and PR121008 are fixed.
+    return [&__func, &__view_iter = *this]<std::size_t _Is>(this auto&& __self) {
+      if (_Is == __view_iter.__it_.index()) {
+        return __func.template operator()<_Is>();
+      }
+      if constexpr (_Is + 1 < sizeof...(_Views)) {
+        return __self.template operator()<_Is + 1>();
+      }
+      __builtin_unreachable();
+    }.template operator()<0>();
+  }
+
+  template <size_t... _Is, typename _Func>
+  _LIBCPP_HIDE_FROM_ABI constexpr void __apply_at_index(size_t __index, _Func&& __func, index_sequence<_Is...>) const {
+    ((__index == _Is ? (static_cast<void>(__func(integral_constant<size_t, _Is>{})), 0) : 0), ...);
+  }
+
+  template <size_t _Idx, typename _Func>
+  _LIBCPP_HIDE_FROM_ABI constexpr void __apply_at_index(size_t __index, _Func&& __func) const {
+    __apply_at_index(__index, std::forward<_Func>(__func), make_index_sequence<_Idx>{});
+  }
+
+  template <class... _Args>
+  _LIBCPP_HIDE_FROM_ABI explicit constexpr __iterator(__maybe_const<_Const, concat_view>* __parent, _Args&&... __args)
+    requires constructible_from<__base_iter, _Args&&...>
+      : __it_(std::forward<_Args>(__args)...), __parent_(__parent) {}
+
+  friend class concat_view;
+  friend class __iterator<!_Const>;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI __iterator() = default;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
+    requires _Const && (convertible_to<iterator_t<_Views>, iterator_t<const _Views>> && ...)
+      : __it_([&__src = __i.__it_]<size_t... _Indices>(size_t __idx, index_sequence<_Indices...>) -> __base_iter {
+          _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+              !__src.valueless_by_exception(), "Trying to convert from a valueless iterator of concat_view.");
+          using __src_lref          = decltype((__src));
+          using __construction_fptr = __base_iter (*)(__src_lref);
+          static constexpr __construction_fptr __vtable[]{[](__src_lref __src_var) -> __base_iter {
+            return __base_iter(in_place_index<_Indices>, std::__unchecked_get<_Indices>(std::move(__src_var)));
+          }...};
+          return __vtable[__idx](__src);
+        }(__i.__it_.index(), make_index_sequence<variant_size_v<__base_iter>>{})),
+        __parent_(__i.__parent_) {}
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+        !__it_.valueless_by_exception(), "Trying to dereference a valueless iterator of concat_view.");
+    return __variant_detail::__visitation::__variant::__visit_value(
+        [](auto&& __it) -> __concat_reference_t<__maybe_const<_Const, _Views>...> { return *__it; }, __it_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+        !__it_.valueless_by_exception(), "Trying to increment a valueless iterator of concat_view.");
+    size_t __active_index = __it_.index();
+    __apply_at_index<variant_size_v<decltype(__it_)>>(__active_index, [&](auto __index_constant) {
+      constexpr size_t __i = __index_constant.value;
+      ++std::__unchecked_get<__i>(__it_);
+      __satisfy<__i>();
+    });
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { ++*this; }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator++(int)
+    requires(__all_forward<_Const, _Views...>)
+  {
+    auto __tmp = *this;
+    ++*this;
+    return __tmp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator--()
+    requires __concat_is_bidirectional<_Const, _Views...>
+  {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+        !__it_.valueless_by_exception(), "Trying to decrement a valueless iterator of concat_view.");
+    size_t __active_index = __it_.index();
+    __apply_at_index<variant_size_v<decltype(__it_)>>(__active_index, [&](auto __index_constant) {
+      constexpr size_t __i = __index_constant.value;
+      __prev<__i>();
+    });
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int)
+    requires __concat_is_bidirectional<_Const, _Views...>
+  {
+    auto __tmp = *this;
+    --*this;
+    return __tmp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __iterator& __y)
+    requires(equality_comparable<iterator_t<__maybe_const<_Const, _Views>>> && ...)
+  {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__x.__it_.valueless_by_exception() && !__y.__it_.valueless_by_exception(),
+                                        "Trying to compare a valueless iterator of concat_view.");
+    return __x.__it_ == __y.__it_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](
diff erence_type __n) const
+    requires __concat_is_random_access<_Const, _Views...>
+  {
+    return *((*this) + __n);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __it, 
diff erence_type __n)
+    requires __concat_is_random_access<_Const, _Views...>
+  {
+    auto __temp = __it;
+    __temp += __n;
+    return __temp;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(
diff erence_type __n, const __iterator& __it)
+    requires __concat_is_random_access<_Const, _Views...>
+  {
+    return __it + __n;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator+=(
diff erence_type __n)
+    requires __concat_is_random_access<_Const, _Views...>
+  {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+        !__it_.valueless_by_exception(), "Trying to increment a valueless iterator of concat_view.");
+    size_t __active_index = __it_.index();
+    if (__n > 0) {
+      __apply_at_index<tuple_size_v<decltype(__parent_->__views_)>>(__active_index, [&](auto __index_constant) {
+        constexpr size_t __i  = __index_constant.value;
+        auto& __active_view   = std::get<__i>(__parent_->__views_);
+        
diff erence_type __idx = std::get<__i>(__it_) - ranges::begin(__active_view);
+        __advance_fwd<__i>(__idx, __n);
+      });
+
+    }
+
+    else if (__n < 0) {
+      __apply_at_index<tuple_size_v<decltype(__parent_->__views_)>>(__active_index, [&](auto __index_constant) {
+        constexpr size_t __i  = __index_constant.value;
+        auto& __active_view   = std::get<__i>(__parent_->__views_);
+        
diff erence_type __idx = std::get<__i>(__it_) - ranges::begin(__active_view);
+        __advance_bwd<__i>(__idx, -__n);
+      });
+    }
+
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator-=(
diff erence_type __n)
+    requires __concat_is_random_access<_Const, _Views...>
+  {
+    *this += -__n;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __it, default_sentinel_t) {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+        !__it.__it_.valueless_by_exception(),
+        "Trying to compare a valueless iterator of concat_view with the default sentinel.");
+    constexpr auto __last_idx = sizeof...(_Views) - 1;
+    return __it.__it_.index() == __last_idx &&
+           std::__unchecked_get<__last_idx>(__it.__it_) == ranges::end(std::get<__last_idx>(__it.__parent_->__views_));
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<(const __iterator& __x, const __iterator& __y)
+    requires(__all_random_access<_Const, _Views> && ...)
+  {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__x.__it_.valueless_by_exception() && !__y.__it_.valueless_by_exception(),
+                                        "Trying to compare a valueless iterator of concat_view.");
+    return __x.__it_ < __y.__it_;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>(const __iterator& __x, const __iterator& __y)
+    requires(__all_random_access<_Const, _Views> && ...)
+  {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__x.__it_.valueless_by_exception() && !__y.__it_.valueless_by_exception(),
+                                        "Trying to compare a valueless iterator of concat_view.");
+    return __x.__it_ > __y.__it_;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<=(const __iterator& __x, const __iterator& __y)
+    requires(__all_random_access<_Const, _Views> && ...)
+  {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__x.__it_.valueless_by_exception() && !__y.__it_.valueless_by_exception(),
+                                        "Trying to compare a valueless iterator of concat_view.");
+    return __x.__it_ <= __y.__it_;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>=(const __iterator& __x, const __iterator& __y)
+    requires(__all_random_access<_Const, _Views> && ...)
+  {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__x.__it_.valueless_by_exception() && !__y.__it_.valueless_by_exception(),
+                                        "Trying to compare a valueless iterator of concat_view.");
+    return __x.__it_ >= __y.__it_;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator<=>(const __iterator& __x, const __iterator& __y)
+    requires((__all_random_access<_Const, _Views> && ...) &&
+             (three_way_comparable<iterator_t<__maybe_const<_Const, _Views>>> && ...))
+  {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__x.__it_.valueless_by_exception() && !__y.__it_.valueless_by_exception(),
+                                        "Trying to compare a valueless iterator of concat_view.");
+    return __x.__it_ <=> __y.__it_;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr decltype(auto) iter_move(const __iterator& __it) noexcept(
+
+      ((is_nothrow_invocable_v< decltype(ranges::iter_move), const iterator_t<__maybe_const<_Const, _Views>>& > &&
+        is_nothrow_convertible_v< range_rvalue_reference_t<__maybe_const<_Const, _Views>>,
+                                  __concat_rvalue_reference_t<__maybe_const<_Const, _Views>...> >) &&
+       ...))
+
+  {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+        !__it.__it_.valueless_by_exception(), "Trying to apply iter_move to a valueless iterator of concat_view.");
+    return __variant_detail::__visitation::__variant::__visit_value(
+        [](const auto& __i) -> __concat_rvalue_reference_t<__maybe_const<_Const, _Views>...> {
+          return ranges::iter_move(__i);
+        },
+        __it.__it_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr void iter_swap(const __iterator& __x, const __iterator& __y)
+
+      noexcept((noexcept(ranges::swap(*__x, *__y))) &&
+               (noexcept(ranges::iter_swap(std::declval<const iterator_t<__maybe_const<_Const, _Views>>>(),
+                                           std::declval<const iterator_t<__maybe_const<_Const, _Views>>>())) &&
+                ...))
+
+    requires swappable_with<iter_reference_t<__iterator>, iter_reference_t<__iterator>> &&
+             (... && indirectly_swappable<iterator_t<__maybe_const<_Const, _Views>>>)
+  {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+        !__x.__it_.valueless_by_exception() && !__y.__it_.valueless_by_exception(),
+        "Trying to swap iterators of concat_view where at least one iterator is valueless.");
+    __variant_detail::__visitation::__variant::__visit_value(
+        [&](const auto& __it1, const auto& __it2) {
+          if constexpr (is_same_v<decltype(__it1), decltype(__it2)>) {
+            ranges::iter_swap(__it1, __it2);
+          } else {
+            ranges::swap(*__x, *__y);
+          }
+        },
+        __x.__it_,
+        __y.__it_);
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr 
diff erence_type
+  operator-(const __iterator& __x, const __iterator& __y)
+    requires __concat_is_random_access<_Const, _Views...>
+  {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+        !__x.__it_.valueless_by_exception() && !__y.__it_.valueless_by_exception(),
+        "Trying to subtract two iterators of concat_view where at least one iterator is valueless.");
+    return __x.__invoke_at_index([&]<std::size_t __index_x>() -> 
diff erence_type {
+      return __y.__invoke_at_index([&]<std::size_t __index_y>() -> 
diff erence_type {
+        if constexpr (__index_x > __index_y) {
+          auto __dx = ranges::distance(
+              ranges::begin(std::get<__index_x>(__x.__parent_->__views_)), std::get<__index_x>(__x.__it_));
+          auto __dy = ranges::distance(
+              std::get<__index_y>(__y.__it_), ranges::end(std::get<__index_y>(__y.__parent_->__views_)));
+          
diff erence_type __s = [&]<std::size_t __start, std::size_t __end>(this auto&& __self) -> 
diff erence_type {
+            if constexpr (__start < __end) {
+              return ranges::size(std::get<__start>(__x.__parent_->__views_)) +
+                     __self.template operator()<__start + 1, __end>();
+            }
+            return 0;
+          }.template operator()<__index_y + 1, __index_x>();
+          return __dy + __s + __dx;
+        } else if constexpr (__index_x < __index_y) {
+          return -(__y - __x);
+        } else {
+          return std::get<__index_x>(__x.__it_) - std::get<__index_y>(__y.__it_);
+        }
+      });
+    });
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __it, 
diff erence_type __n)
+    requires __concat_is_random_access<_Const, _Views...>
+  {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+        !__it.__it_.valueless_by_exception(), "Trying to subtract a valuess iterators of concat_view.");
+    auto __temp = __it;
+    __temp -= __n;
+    return __temp;
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr 
diff erence_type
+  operator-(const __iterator& __x, default_sentinel_t)
+    requires(sized_sentinel_for<sentinel_t<__maybe_const<_Const, _Views>>, iterator_t<__maybe_const<_Const, _Views>>> &&
+             ...) &&
+            (__all_but_first_model_sized_range<_Const, _Views...>::value)
+  {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+        !__x.__it_.valueless_by_exception(),
+        "Trying to subtract a valuess iterators of concat_view from the default sentinel.");
+    return __x.__invoke_at_index([&]<std::size_t __index_x>() -> 
diff erence_type {
+      auto __dx =
+          ranges::distance(std::get<__index_x>(__x.__it_), ranges::end(std::get<__index_x>(__x.__parent_->__views_)));
+      
diff erence_type __s = [&]<std::size_t __start, std::size_t __end>(this auto&& __self) -> 
diff erence_type {
+        if constexpr (__start < __end) {
+          return ranges::size(std::get<__start>(__x.__parent_->__views_)) +
+                 __self.template operator()<__start + 1, __end>();
+        }
+        return 0;
+      }.template operator()<__index_x + 1, sizeof...(_Views)>();
+      return -(__dx + __s);
+    });
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr 
diff erence_type
+  operator-(default_sentinel_t, const __iterator& __x)
+    requires(sized_sentinel_for<sentinel_t<__maybe_const<_Const, _Views>>, iterator_t<__maybe_const<_Const, _Views>>> &&
+             ...) &&
+            (__all_but_first_model_sized_range<_Const, _Views...>::value)
+  {
+    return -(__x - default_sentinel);
+  }
+};
+
+namespace views {
+namespace __concat {
+struct __fn {
+  template <input_range _Range>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto
+  operator()(_Range&& __range) noexcept(noexcept(views::all((std::forward<_Range>(__range)))))
+      -> decltype(views::all((std::forward<_Range>(__range)))) {
+    return views::all(std::forward<_Range>(__range));
+  }
+
+  template <class _FirstRange, class... _TailRanges>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto
+  operator()(_FirstRange&& __first, _TailRanges&&... __tail) noexcept(
+      noexcept(concat_view(std::forward<_FirstRange>(__first), std::forward<_TailRanges>(__tail)...)))
+      -> decltype(concat_view(std::forward<_FirstRange>(__first), std::forward<_TailRanges>(__tail)...)) {
+    return concat_view(std::forward<_FirstRange>(__first), std::forward<_TailRanges>(__tail)...);
+  }
+};
+} // namespace __concat
+
+inline namespace __cpo {
+inline constexpr auto concat = __concat::__fn{};
+} // namespace __cpo
+} // namespace views
+
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___RANGES_CONCAT_VIEW_H

diff  --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 51a2b77a9ba45..2ccb455b7408d 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1886,6 +1886,7 @@ module std [system] {
       export std.functional.bind_back
     }
     module common_view                    { header "__ranges/common_view.h" }
+    module concat_view                    { header "__ranges/concat_view.h" }
     module concepts                       { header "__ranges/concepts.h" }
     module container_compatible_range     { header "__ranges/container_compatible_range.h" }
     module counted {

diff  --git a/libcxx/include/ranges b/libcxx/include/ranges
index 308224427db60..de05c31744fcf 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -201,6 +201,16 @@ namespace std::ranges {
     inline constexpr unspecified filter = unspecified;
   }
 
+  // [range.concat], concat view
+  template <input_range... Views>
+      requires (view<Views> && ...) && (sizeof...(Views) > 0) &&
+                concatable<Views...>
+  class concat_view;
+
+  namespace views {
+    inline constexpr unspecified concat = unspecified;
+  }
+
   // [range.drop], drop view
   template<view V>
     class drop_view;
@@ -514,6 +524,10 @@ namespace std {
 #    include <__ranges/zip_view.h>
 #  endif
 
+#  if _LIBCPP_STD_VER >= 26
+#    include <__ranges/concat_view.h>
+#  endif
+
 #  include <version>
 
 // standard-mandated includes

diff  --git a/libcxx/include/version b/libcxx/include/version
index 1c683b67e5700..471eb3c104b53 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -614,7 +614,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # undef  __cpp_lib_out_ptr
 # define __cpp_lib_out_ptr                              202311L
 // # define __cpp_lib_philox_engine                        202406L
-// # define __cpp_lib_ranges_concat                        202403L
+# define __cpp_lib_ranges_concat                        202403L
 # define __cpp_lib_ranges_indices                       202506L
 # define __cpp_lib_ratio                                202306L
 // # define __cpp_lib_rcu                                  202306L

diff  --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc
index 576f0650438e3..876942bdffbf5 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -181,6 +181,15 @@ export namespace std {
     } // namespace views
 #endif // _LIBCPP_STD_VER >= 23
 
+#if _LIBCPP_STD_VER >= 26
+    // [range.concat.view], concat_view
+    using std::ranges::concat_view;
+
+    namespace views {
+      using std::ranges::views::concat;
+    } // namespace views
+#endif // _LIBCPP_STD_VER >= 26
+
     // [range.filter], filter view
     using std::ranges::filter_view;
 

diff  --git a/libcxx/test/libcxx/ranges/range.adaptors/range.concat/iterator.valueless_by_exception.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.concat/iterator.valueless_by_exception.pass.cpp
new file mode 100644
index 0000000000000..8e0ff79e16a27
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.concat/iterator.valueless_by_exception.pass.cpp
@@ -0,0 +1,659 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: has-unix-headers, libcpp-hardening-mode={{extensive|debug}}
+// REQUIRES: std-at-least-c++26
+// UNSUPPORTED: libcpp-hardening-mode=none
+
+#include <iostream>
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "check_assertion.h"
+#include "double_move_tracker.h"
+#include "test_iterators.h"
+
+int val[] = {1, 2, 3};
+
+bool flag = false;
+
+template <std::size_t N>
+struct Iter;
+
+template <std::size_t N>
+struct Iter {
+  using value_type        = int;
+  using 
diff erence_type   = std::ptr
diff _t;
+  using reference         = int&;
+  using pointer           = int*;
+  using iterator_category = std::random_access_iterator_tag;
+  using iterator_concept  = std::random_access_iterator_tag;
+
+private:
+  int* ptr_ = nullptr;
+
+  template <std::size_t M>
+  friend struct Iter;
+
+public:
+  Iter() = default;
+  Iter(int* ptr) : ptr_(ptr) {}
+  Iter(const Iter&) = default;
+  Iter(Iter&& other) : ptr_(other.ptr_) {
+    if (flag)
+      throw 5;
+  }
+
+  Iter& operator=(const Iter&) = default;
+  Iter& operator=(Iter&& o) {
+    ptr_ = o.ptr_;
+    if (flag)
+      throw 5;
+    return *this;
+  }
+
+  reference operator*() const { return *ptr_; }
+  pointer operator->() const { return ptr_; }
+  reference operator[](
diff erence_type n) const { return ptr_[n]; }
+
+  Iter& operator++() {
+    ++ptr_;
+    return *this;
+  }
+  Iter operator++(int) {
+    auto tmp = *this;
+    ++*this;
+    return tmp;
+  }
+  Iter& operator--() {
+    --ptr_;
+    return *this;
+  }
+  Iter operator--(int) {
+    auto tmp = *this;
+    --*this;
+    return tmp;
+  }
+
+  Iter& operator+=(
diff erence_type n) {
+    ptr_ += n;
+    return *this;
+  }
+  Iter& operator-=(
diff erence_type n) {
+    ptr_ -= n;
+    return *this;
+  }
+
+  template <std::size_t X>
+  friend Iter<X> operator+(Iter<X> it, 
diff erence_type n);
+
+  template <std::size_t X>
+  friend Iter<X> operator+(
diff erence_type n, Iter<X> it);
+
+  template <std::size_t X>
+  friend Iter<X> operator-(Iter<X> it, 
diff erence_type n);
+
+  template <std::size_t X, std::size_t Y>
+  friend 
diff erence_type operator-(Iter<X> a, Iter<Y> b);
+
+  friend bool operator==(Iter a, Iter b) { return a.ptr_ == b.ptr_; };
+  friend bool operator<(Iter a, Iter b) { return a.ptr_ < b.ptr_; }
+  friend bool operator>(Iter a, Iter b) { return a.ptr_ > b.ptr_; }
+  friend bool operator<=(Iter a, Iter b) { return a.ptr_ <= b.ptr_; }
+  friend bool operator>=(Iter a, Iter b) { return a.ptr_ >= b.ptr_; }
+  friend auto operator<=>(Iter a, Iter b) { return a.ptr_ <=> b.ptr_; }
+};
+
+template <std::size_t X>
+inline Iter<X> operator+(Iter<X> it, std::ptr
diff _t n) {
+  return Iter<X>(it.ptr_ + n);
+}
+
+template <std::size_t X>
+inline Iter<X> operator+(std::ptr
diff _t n, Iter<X> it) {
+  return Iter<X>(it.ptr_ + n);
+}
+
+template <std::size_t X>
+inline Iter<X> operator-(Iter<X> it, std::ptr
diff _t n) {
+  return Iter<X>(it.ptr_ - n);
+}
+
+template <std::size_t X, std::size_t Y>
+inline std::ptr
diff _t operator-(Iter<X> a, Iter<Y> b) {
+  return a.ptr_ - b.ptr_;
+}
+
+template <std::size_t N>
+struct Range : std::ranges::view_base {
+  using iterator       = Iter<N>;
+  using const_iterator = Iter<N>;
+  using sentinel       = sentinel_wrapper<iterator>;
+
+  int* data_;
+  std::size_t size_;
+
+  Range() : data_(val), size_(4) {}
+
+  Range(int* data, std::size_t size) : data_(data), size_(size) {}
+
+  iterator begin() { return iterator(data_); }
+  iterator end() { return iterator(data_ + size_); }
+
+  const_iterator begin() const { return const_iterator(data_); }
+  const_iterator end() const { return const_iterator(data_ + size_); }
+};
+
+template <std::size_t N, typename T>
+struct NonSimpleIter {
+  using 
diff erence_type = ptr
diff _t;
+  using value_type      = T;
+
+  T* ptr_ = nullptr;
+
+  NonSimpleIter() = default;
+  NonSimpleIter(T* ptr) : ptr_(ptr) {}
+
+  template <class U>
+    requires(std::is_const_v<T> && !std::is_const_v<U> &&
+             std::is_same_v<std::remove_const_t<U>, std::remove_const_t<T>>)
+  NonSimpleIter(const NonSimpleIter<N, U>& other) : ptr_(other.ptr_) {}
+
+  NonSimpleIter(const NonSimpleIter&) = default;
+  NonSimpleIter(NonSimpleIter&& other) : ptr_(other.ptr_) {
+    if (flag)
+      throw 5;
+  }
+
+  NonSimpleIter& operator=(const NonSimpleIter&) = default;
+  NonSimpleIter& operator=(NonSimpleIter&& o) {
+    ptr_ = o.ptr_;
+    if (flag)
+      throw 5;
+    return *this;
+  }
+
+  T& operator*() const { return *ptr_; }
+  NonSimpleIter& operator++() {
+    ++ptr_;
+    return *this;
+  }
+  NonSimpleIter operator++(int) {
+    auto tmp = *this;
+    ++*this;
+    return tmp;
+  }
+
+  friend bool operator==(NonSimpleIter, NonSimpleIter) = default;
+};
+
+template <std::size_t N, typename T>
+struct NonSimpleRange : std::ranges::view_base {
+  NonSimpleIter<N, T> begin() { return &val[0]; }
+  NonSimpleIter<N, T> end() { return &val[3]; }
+  NonSimpleIter<N, const T> begin() const { return &val[0]; }
+  NonSimpleIter<N, const T> end() const { return &val[3]; }
+};
+
+static_assert(std::ranges::range<Range<0>>);
+static_assert(std::ranges::sized_range<Range<0>>);
+
+int main() {
+  {
+    // valueless by exception test operator*
+    Range<0> r1;
+    Range<1> r2;
+
+    auto cv    = std::views::concat(r1, r2);
+    auto iter1 = cv.begin();
+    auto iter2 = std::ranges::next(cv.begin(), 4);
+    flag       = true;
+    try {
+      iter1 = std::move(iter2);
+      assert(false);
+    } catch (...) {
+      TEST_LIBCPP_ASSERT_FAILURE([=] { (void)*iter1; }(), "Trying to dereference a valueless iterator of concat_view.");
+    }
+  }
+
+  {
+    // valueless by exception test operator==
+    flag = false;
+    Range<0> r1;
+    Range<1> r2;
+
+    auto cv    = std::views::concat(r1, r2);
+    auto iter1 = cv.begin();
+    auto iter2 = std::ranges::next(cv.begin(), 4);
+    auto iter3 = cv.begin();
+    flag       = true;
+    try {
+      iter1 = std::move(iter2);
+      assert(false);
+    } catch (...) {
+      TEST_LIBCPP_ASSERT_FAILURE(
+          [=] { (void)(iter1 == iter3); }(), "Trying to compare a valueless iterator of concat_view.");
+    }
+  }
+
+  {
+    // valueless by exception test operator== with a sentinel
+    flag = false;
+    Range<0> r1;
+    Range<1> r2;
+    auto cv    = std::views::concat(r1, r2);
+    auto iter1 = cv.begin();
+    auto iter2 = std::ranges::next(cv.begin(), 4);
+    flag       = true;
+    try {
+      iter1 = std::move(iter2);
+      assert(false);
+    } catch (...) {
+      TEST_LIBCPP_ASSERT_FAILURE([=] { (void)(iter1 == std::default_sentinel); }(),
+                                 "Trying to compare a valueless iterator of concat_view with the default sentinel.");
+    }
+  }
+
+  {
+    // valueless by exception test operator--
+    flag = false;
+    Range<0> r1;
+    Range<1> r2;
+
+    auto cv    = std::views::concat(r1, r2);
+    auto iter1 = cv.begin();
+    auto iter2 = std::ranges::next(cv.begin(), 4);
+    flag       = true;
+    try {
+      iter1 = std::move(iter2);
+      assert(false);
+    } catch (...) {
+      TEST_LIBCPP_ASSERT_FAILURE([&] { --iter1; }(), "Trying to decrement a valueless iterator of concat_view.");
+    }
+  }
+
+  {
+    // valueless by exception test operator--(int)
+    flag = false;
+    Range<0> r1;
+    Range<1> r2;
+
+    auto cv    = std::views::concat(r1, r2);
+    auto iter1 = cv.begin();
+    auto iter2 = std::ranges::next(cv.begin(), 4);
+    flag       = true;
+    try {
+      iter1 = std::move(iter2);
+      assert(false);
+    } catch (...) {
+      TEST_LIBCPP_ASSERT_FAILURE([&] { iter1--; }(), "Trying to decrement a valueless iterator of concat_view.");
+    }
+  }
+
+  {
+    // valueless by exception test operator++(int)
+    flag = false;
+    Range<0> r1;
+    Range<1> r2;
+
+    auto cv    = std::views::concat(r1, r2);
+    auto iter1 = cv.begin();
+    auto iter2 = std::ranges::next(cv.begin(), 4);
+    flag       = true;
+    try {
+      iter1 = std::move(iter2);
+      assert(false);
+    } catch (...) {
+      TEST_LIBCPP_ASSERT_FAILURE([&] { iter1++; }(), "Trying to increment a valueless iterator of concat_view.");
+    }
+  }
+
+  {
+    // valueless by exception test operator++
+    flag = false;
+    Range<0> r1;
+    Range<1> r2;
+
+    auto cv    = std::views::concat(r1, r2);
+    auto iter1 = cv.begin();
+    auto iter2 = std::ranges::next(cv.begin(), 4);
+    flag       = true;
+    try {
+      iter1 = std::move(iter2);
+      assert(false);
+    } catch (...) {
+      TEST_LIBCPP_ASSERT_FAILURE([&] { ++iter1; }(), "Trying to increment a valueless iterator of concat_view.");
+    }
+  }
+
+  {
+    // valueless by exception test operator+=
+    flag = false;
+    Range<0> r1;
+    Range<1> r2;
+
+    auto cv    = std::views::concat(r1, r2);
+    auto iter1 = cv.begin();
+    auto iter2 = std::ranges::next(cv.begin(), 4);
+    flag       = true;
+    try {
+      iter1 = std::move(iter2);
+      assert(false);
+    } catch (...) {
+      TEST_LIBCPP_ASSERT_FAILURE([&] { iter1 += 1; }(), "Trying to increment a valueless iterator of concat_view.");
+    }
+  }
+
+  {
+    // valueless by exception test operator-=
+    // this one eventually calls operator+= inside the function
+    flag = false;
+    Range<0> r1;
+    Range<1> r2;
+
+    auto cv    = std::views::concat(r1, r2);
+    auto iter1 = cv.begin();
+    auto iter2 = std::ranges::next(cv.begin(), 4);
+    flag       = true;
+    try {
+      iter1 = std::move(iter2);
+      assert(false);
+    } catch (...) {
+      TEST_LIBCPP_ASSERT_FAILURE([&] { iter1 -= 1; }(), "Trying to increment a valueless iterator of concat_view.");
+    }
+  }
+
+  {
+    // valueless by exception test operator-=
+    // this one eventually calls operator+= inside the function
+    flag = false;
+    Range<0> r1;
+    Range<1> r2;
+
+    auto cv    = std::views::concat(r1, r2);
+    auto iter1 = cv.begin();
+    auto iter2 = std::ranges::next(cv.begin(), 4);
+    flag       = true;
+    try {
+      iter1 = std::move(iter2);
+      assert(false);
+    } catch (...) {
+      TEST_LIBCPP_ASSERT_FAILURE([&] { (void)iter1[1]; }(), "Trying to increment a valueless iterator of concat_view.");
+    }
+  }
+
+  {
+    // valueless by exception test operator+(it, n)
+    flag = false;
+    Range<0> r1;
+    Range<1> r2;
+
+    auto cv    = std::views::concat(r1, r2);
+    auto iter1 = cv.begin();
+    auto iter2 = std::ranges::next(cv.begin(), 4);
+    flag       = true;
+    try {
+      iter1 = std::move(iter2);
+      assert(false);
+    } catch (...) {
+      TEST_LIBCPP_ASSERT_FAILURE([&] { [[maybe_unused]] auto iter3 = iter1 + 1; }(),
+                                 "Trying to increment a valueless iterator of concat_view.");
+    }
+  }
+
+  {
+    // valueless by exception test operator+(n, it)
+    flag = false;
+    Range<0> r1;
+    Range<1> r2;
+
+    auto cv    = std::views::concat(r1, r2);
+    auto iter1 = cv.begin();
+    auto iter2 = std::ranges::next(cv.begin(), 4);
+    flag       = true;
+    try {
+      iter1 = std::move(iter2);
+      assert(false);
+    } catch (...) {
+      TEST_LIBCPP_ASSERT_FAILURE([&] { [[maybe_unused]] auto iter3 = iter1 + 1; }(),
+                                 "Trying to increment a valueless iterator of concat_view.");
+    }
+  }
+
+  {
+    // valueless by exception test operator-(it, default_sentinel)
+    flag = false;
+    Range<0> r1;
+    Range<1> r2;
+
+    auto cv    = std::views::concat(r1, r2);
+    auto iter1 = cv.begin();
+    auto iter2 = std::ranges::next(cv.begin(), 4);
+    flag       = true;
+    try {
+      iter1 = std::move(iter2);
+      assert(false);
+    } catch (...) {
+      TEST_LIBCPP_ASSERT_FAILURE([&] { [[maybe_unused]] auto iter3 = iter1 - std::default_sentinel_t{}; }(),
+                                 "Trying to subtract a valuess iterators of concat_view from the default sentinel.");
+    }
+  }
+
+  {
+    // valueless by exception test operator-(default_sentinel, it)
+    flag = false;
+    Range<0> r1;
+    Range<1> r2;
+
+    auto cv    = std::views::concat(r1, r2);
+    auto iter1 = cv.begin();
+    auto iter2 = std::ranges::next(cv.begin(), 4);
+    flag       = true;
+    try {
+      iter1 = std::move(iter2);
+      assert(false);
+    } catch (...) {
+      TEST_LIBCPP_ASSERT_FAILURE([&] { [[maybe_unused]] auto iter3 = iter1 - std::default_sentinel_t{}; }(),
+                                 "Trying to subtract a valuess iterators of concat_view from the default sentinel.");
+    }
+  }
+
+  {
+    // valueless by exception test operator>
+    flag = false;
+    Range<0> r1;
+    Range<1> r2;
+
+    auto cv    = std::views::concat(r1, r2);
+    auto iter1 = cv.begin();
+    auto iter2 = std::ranges::next(cv.begin(), 4);
+    flag       = true;
+    try {
+      iter1 = std::move(iter2);
+      assert(false);
+    } catch (...) {
+      TEST_LIBCPP_ASSERT_FAILURE(
+          [&] { (void)(iter1 > iter2); }(), "Trying to compare a valueless iterator of concat_view.");
+    }
+  }
+
+  {
+    // valueless by exception test operator>=
+    flag = false;
+    Range<0> r1;
+    Range<1> r2;
+
+    auto cv    = std::views::concat(r1, r2);
+    auto iter1 = cv.begin();
+    auto iter2 = std::ranges::next(cv.begin(), 4);
+    flag       = true;
+    try {
+      iter1 = std::move(iter2);
+      assert(false);
+    } catch (...) {
+      TEST_LIBCPP_ASSERT_FAILURE(
+          [&] { (void)(iter1 >= iter2); }(), "Trying to compare a valueless iterator of concat_view.");
+    }
+  }
+
+  {
+    // valueless by exception test operator<
+    flag = false;
+    Range<0> r1;
+    Range<1> r2;
+
+    auto cv    = std::views::concat(r1, r2);
+    auto iter1 = cv.begin();
+    auto iter2 = std::ranges::next(cv.begin(), 4);
+    flag       = true;
+    try {
+      iter1 = std::move(iter2);
+      assert(false);
+    } catch (...) {
+      TEST_LIBCPP_ASSERT_FAILURE(
+          [&] { (void)(iter1 < iter2); }(), "Trying to compare a valueless iterator of concat_view.");
+    }
+  }
+
+  {
+    // valueless by exception test operator<=
+    flag = false;
+    Range<0> r1;
+    Range<1> r2;
+
+    auto cv    = std::views::concat(r1, r2);
+    auto iter1 = cv.begin();
+    auto iter2 = std::ranges::next(cv.begin(), 4);
+    flag       = true;
+    try {
+      iter1 = std::move(iter2);
+      assert(false);
+    } catch (...) {
+      TEST_LIBCPP_ASSERT_FAILURE(
+          [&] { (void)(iter1 <= iter2); }(), "Trying to compare a valueless iterator of concat_view.");
+    }
+  }
+
+  {
+    // valueless by exception test operator<=>
+    flag = false;
+    Range<0> r1;
+    Range<1> r2;
+
+    auto cv    = std::views::concat(r1, r2);
+    auto iter1 = cv.begin();
+    auto iter2 = std::ranges::next(cv.begin(), 4);
+    flag       = true;
+    try {
+      iter1 = std::move(iter2);
+      assert(false);
+    } catch (...) {
+      TEST_LIBCPP_ASSERT_FAILURE(
+          [&] { (void)(iter1 <=> iter2); }(), "Trying to compare a valueless iterator of concat_view.");
+    }
+  }
+
+  {
+    // valueless by exception test operator- between two iterators
+    flag = false;
+    Range<0> r1;
+    Range<1> r2;
+
+    auto cv    = std::views::concat(r1, r2);
+    auto iter1 = cv.begin();
+    auto iter2 = std::ranges::next(cv.begin(), 4);
+    flag       = true;
+    try {
+      iter1 = std::move(iter2);
+      assert(false);
+    } catch (...) {
+      TEST_LIBCPP_ASSERT_FAILURE(
+          [&] { (void)(iter1 - iter2); }(),
+          "Trying to subtract two iterators of concat_view where at least one iterator is valueless.");
+    }
+  }
+
+  {
+    // valueless by exception test operator- with a constant
+    flag = false;
+    Range<0> r1;
+    Range<1> r2;
+
+    auto cv    = std::views::concat(r1, r2);
+    auto iter1 = cv.begin();
+    auto iter2 = std::ranges::next(cv.begin(), 4);
+    flag       = true;
+    try {
+      iter1 = std::move(iter2);
+    } catch (...) {
+      TEST_LIBCPP_ASSERT_FAILURE(
+          [&] { (void)(iter1 - 1); }(), "Trying to subtract a valuess iterators of concat_view.");
+    }
+  }
+
+  {
+    // valueless by exception test iter_move(it)
+    flag = false;
+    Range<0> r1;
+    Range<1> r2;
+
+    auto cv    = std::views::concat(r1, r2);
+    auto iter1 = cv.begin();
+    auto iter2 = std::ranges::next(cv.begin(), 4);
+    flag       = true;
+    try {
+      iter1 = std::move(iter2);
+      assert(false);
+    } catch (...) {
+      TEST_LIBCPP_ASSERT_FAILURE([&] { [[maybe_unused]] auto iter3 = std::ranges::iter_move(iter1); }(),
+                                 "Trying to apply iter_move to a valueless iterator of concat_view.");
+    }
+  }
+
+  {
+    // valueless by exception test iter_swap(iter1, iter2)
+    flag = false;
+    Range<0> r1;
+    Range<1> r2;
+
+    auto cv    = std::views::concat(r1, r2);
+    auto iter1 = cv.begin();
+    auto iter2 = std::ranges::next(cv.begin(), 4);
+    flag       = true;
+    try {
+      iter1 = std::move(iter2);
+      assert(false);
+    } catch (...) {
+      TEST_LIBCPP_ASSERT_FAILURE([&] { std::ranges::iter_swap(iter1, iter2); }(),
+                                 "Trying to swap iterators of concat_view where at least one iterator is valueless.");
+    }
+  }
+
+  {
+    // valueless by exception test constructor
+    flag = false;
+    NonSimpleRange<0, int> r1;
+    NonSimpleRange<1, int> r2;
+
+    auto cv     = std::views::concat(r1, r2);
+    auto iter1  = cv.begin();
+    auto iter2  = std::ranges::next(cv.begin(), 4);
+    flag        = true;
+    using Iter  = std::ranges::iterator_t<decltype(cv)>;       // iterator<false>
+    using CIter = std::ranges::iterator_t<const decltype(cv)>; // iterator<true>
+
+    try {
+      iter1 = std::move(iter2);
+      assert(false);
+    } catch (...) {
+      TEST_LIBCPP_ASSERT_FAILURE(
+          [&] { [[maybe_unused]] CIter it3(iter1); }(), "Trying to convert from a valueless iterator of concat_view.");
+    }
+  }
+}

diff  --git a/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..b81e646d381c4
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp
@@ -0,0 +1,70 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// Test that concat_view and its iterator's functions are marked [[nodiscard]].
+
+#include <array>
+#include <ranges>
+#include <utility>
+#include <vector>
+
+void test() {
+  std::array<int, 3> a{1, 2, 3};
+  std::vector<int> b{4, 5, 6};
+
+  std::ranges::concat_view cv(a, b);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  cv.begin();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(cv).begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  cv.end();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(cv).end();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  cv.size();
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::as_const(cv).size();
+
+  // [range.concat.iterator]
+
+  auto it = cv.begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it[2];
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it + 1;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  1 + it;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - 1;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - it;
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  it - std::default_sentinel;
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::default_sentinel - it;
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  iter_move(it);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::concat(a);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::views::concat(a, b);
+}

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 6825f9675d459..670b0e664721a 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
@@ -447,17 +447,11 @@
 #    error "__cpp_lib_ranges_chunk_by should have the value 202202L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_ranges_concat
-#      error "__cpp_lib_ranges_concat should be defined in c++26"
-#    endif
-#    if __cpp_lib_ranges_concat != 202403L
-#      error "__cpp_lib_ranges_concat should have the value 202403L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_ranges_concat
-#      error "__cpp_lib_ranges_concat should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_ranges_concat
+#    error "__cpp_lib_ranges_concat should be defined in c++26"
+#  endif
+#  if __cpp_lib_ranges_concat != 202403L
+#    error "__cpp_lib_ranges_concat should have the value 202403L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_ranges_enumerate

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 dfee4b6d458db..2aa52a64c6cf0 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
@@ -7691,17 +7691,11 @@
 #    error "__cpp_lib_ranges_chunk_by should have the value 202202L in c++26"
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    ifndef __cpp_lib_ranges_concat
-#      error "__cpp_lib_ranges_concat should be defined in c++26"
-#    endif
-#    if __cpp_lib_ranges_concat != 202403L
-#      error "__cpp_lib_ranges_concat should have the value 202403L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_ranges_concat
-#      error "__cpp_lib_ranges_concat should not be defined because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_ranges_concat
+#    error "__cpp_lib_ranges_concat should be defined in c++26"
+#  endif
+#  if __cpp_lib_ranges_concat != 202403L
+#    error "__cpp_lib_ranges_concat should have the value 202403L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_ranges_contains

diff  --git a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
index 9443ca6c90a30..d83ca7f2eabfc 100644
--- a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
+++ b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
@@ -137,6 +137,6 @@ static_assert(test(std::views::zip, a, a));
 
 #if TEST_STD_VER >= 26
 // static_assert(test(std::views::cache_latest, a));
-// static_assert(test(std::views::concat, a, a));
+static_assert(test(std::views::concat, a, a));
 // static_assert(test(std::views::to_input, a));
 #endif

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.concat/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/adaptor.pass.cpp
new file mode 100644
index 0000000000000..b0358856a07c8
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/adaptor.pass.cpp
@@ -0,0 +1,73 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// std::views::concat
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <iterator>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include "../range_adaptor_types.h"
+
+static_assert(!std::is_invocable_v<decltype((std::views::concat))>);
+static_assert(!std::is_invocable_v<decltype((std::views::concat)), int>);
+static_assert(std::is_invocable_v<decltype((std::views::concat)), SizedRandomAccessView>);
+static_assert(std::is_invocable_v<decltype((std::views::concat)), SizedRandomAccessView, NonSimpleCommon>);
+static_assert(std::is_invocable_v<decltype((std::views::concat)),
+                                  SizedRandomAccessView,
+                                  NonSimpleCommon,
+                                  NonSimpleBidiCommonView>);
+static_assert(!std::is_invocable_v<decltype((std::views::concat)), SizedRandomAccessView, int>);
+
+constexpr bool test() {
+  {
+    // single range
+    int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+    std::same_as<decltype(std::views::all((std::forward<SizedRandomAccessView>(buffer))))> decltype(auto) v =
+        std::ranges::views::concat(SizedRandomAccessView{buffer});
+    assert(std::ranges::size(v) == 8);
+    static_assert(std::is_same_v<std::ranges::range_reference_t<decltype(v)>, int&>);
+  }
+
+  {
+    // single view as output range will be rejected
+    // https://cplusplus.github.io/LWG/issue4082
+    std::vector<int> v{1, 2, 3};
+    static_assert(
+        !std::is_invocable_v<decltype((std::views::concat)), decltype(std::views::counted(std::back_inserter(v), 3))>);
+  }
+
+  {
+    // more than one ranges
+    int buffer[4] = {1, 2, 3, 4};
+    std::array<int, 3> a{1, 2, 3};
+    std::same_as<std::ranges::concat_view<NonSimpleCommonRandomAccessSized,
+                                          std::ranges::ref_view<std::array<int, 3>>>> decltype(auto) v =
+        std::ranges::views::concat(NonSimpleCommonRandomAccessSized{buffer}, a);
+    assert(&(*v.begin()) == &(buffer[0]));
+    assert(&(*(v.begin() + 4)) == &(a[0]));
+    static_assert(std::is_same_v<std::ranges::range_reference_t<decltype(v)>, int&>);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.concat/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/begin.pass.cpp
new file mode 100644
index 0000000000000..0c6081fd70c4f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/begin.pass.cpp
@@ -0,0 +1,119 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// constexpr __iterator<false> begin()
+//     requires(!(__simple_view<_Views> && ...))
+
+// constexpr __iterator<true> begin() const
+//     requires((range<const _Views> && ...) && __concatable<const _Views...>)
+
+#include <array>
+#include <ranges>
+#include <vector>
+
+#include <cassert>
+#include "test_iterators.h"
+#include "types.h"
+#include "../range_adaptor_types.h"
+
+template <class T>
+concept HasConstBegin = requires(const T& ct) { ct.begin(); };
+
+template <class T>
+concept HasBegin = requires(T& t) { t.begin(); };
+
+template <class T>
+concept HasConstAndNonConstBegin = HasConstBegin<T> && requires(T& t, const T& ct) {
+  requires !std::same_as<decltype(t.begin()), decltype(ct.begin())>;
+};
+
+template <class T>
+concept HasOnlyNonConstBegin = HasBegin<T> && !HasConstBegin<T>;
+
+template <class T>
+concept HasOnlyConstBegin = HasConstBegin<T> && !HasConstAndNonConstBegin<T>;
+
+constexpr bool test() {
+  // check the case of simple view
+  {
+    int buffer[4] = {1, 2, 3, 4};
+    std::ranges::concat_view v(SimpleCommon{buffer}, SimpleCommon{buffer});
+    static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+    assert(v.begin() == std::as_const(v).begin());
+    assert(*v.begin() == buffer[0]);
+    assert(*std::as_const(v).begin() == buffer[0]);
+
+    using View = decltype(v);
+    static_assert(HasOnlyConstBegin<View>);
+    static_assert(!HasOnlyNonConstBegin<View>);
+    static_assert(!HasConstAndNonConstBegin<View>);
+  }
+
+  // not all underlying ranges model simple view
+  {
+    int buffer[4] = {1, 2, 3, 4};
+    std::ranges::concat_view v(SimpleCommon{buffer}, NonSimpleNonCommon{buffer});
+    static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+    assert(v.begin() == std::as_const(v).begin());
+    assert(*v.begin() == buffer[0]);
+    assert(*std::as_const(v).begin() == buffer[0]);
+
+    using View = decltype(v);
+    static_assert(!HasOnlyConstBegin<View>);
+    static_assert(!HasOnlyNonConstBegin<View>);
+    static_assert(HasConstAndNonConstBegin<View>);
+  }
+
+  // first view is empty
+  {
+    std::vector<int> v1;
+    std::vector<int> v2 = {1, 2, 3, 4};
+    std::ranges::concat_view view(v1, v2);
+    auto it = view.begin();
+    assert(*it == 1);
+    assert(it + 4 == view.end());
+  }
+
+  // first few views is empty, including 
diff erent types
+  {
+    std::vector<int> v1;
+    std::array<int, 0> v2;
+    std::vector<int> v3 = {1, 2, 3, 4};
+    std::ranges::concat_view view(v1, v2, v3);
+    auto it = view.begin();
+    assert(*it == 1);
+    assert(it + 4 == view.end());
+  }
+
+  // all views are empty
+  {
+    std::array<int, 0> arr;
+    std::vector<int> v;
+    std::ranges::concat_view(arr, v);
+    assert(v.begin() == v.end());
+  }
+
+  // testing concatable constraint
+  {
+    static_assert(!ConcatableConstViews<ViewWithNoConstBegin>);
+    static_assert(ConcatableConstViews<ViewWithConstBegin>);
+    static_assert(!ConcatableConstViews<ViewWithNoConstBegin, ViewWithConstBegin>);
+    static_assert(!ConcatableConstViews<ViewWithNoConstBegin, ViewWithConstBegin, SizedViewWithConstBegin>);
+    static_assert(ConcatableConstViews<ViewWithConstBegin, SizedViewWithConstBegin>);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.concat/constraints.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/constraints.pass.cpp
new file mode 100644
index 0000000000000..0792c8c7c921a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/constraints.pass.cpp
@@ -0,0 +1,288 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+#include <cassert>
+#include <list>
+#include <ranges>
+#include <string>
+#include <type_traits>
+#include <vector>
+#include "test_iterators.h"
+#include "test_macros.h"
+
+// test concept constraints
+
+template <typename T>
+concept WellFormedView = requires(T& a) { std::views::concat(a); };
+
+struct X {};
+struct Y {};
+
+struct BadIter {
+  using value_type = int;
+  int* p;
+
+  BadIter() = default;
+  explicit BadIter(int* q) : p(q) {}
+
+  int& operator*() const { return *p; }
+  BadIter& operator++() {
+    ++p;
+    return *this;
+  }
+  void operator++(int) { ++p; }
+
+  friend bool operator==(const BadIter& a, const BadIter& b) { return a.p == b.p; }
+  friend bool operator!=(const BadIter& a, const BadIter& b) { return !(a == b); }
+
+  friend X iter_move(const BadIter&) { return X{}; }
+};
+
+struct BadView : std::ranges::view_base {
+  int buf_[1] = {0};
+  BadIter begin() const { return BadIter(const_cast<int*>(buf_)); }
+  BadIter end() const { return BadIter(const_cast<int*>(buf_ + 1)); }
+};
+
+struct InputRange {
+  using Iterator = cpp17_input_iterator<int*>;
+  using Sentinel = sentinel_wrapper<Iterator>;
+  constexpr InputRange(int* b, int* e) : begin_(b), end_(e) {}
+  constexpr Iterator begin() { return Iterator(begin_); }
+  constexpr Sentinel end() { return Sentinel(Iterator(end_)); }
+
+private:
+  int* begin_;
+  int* end_;
+};
+
+struct RefOnlyRange1 : std::ranges::view_base {
+  X* begin() const;
+  X* end() const;
+};
+
+struct RefOnlyRange2 : std::ranges::view_base {
+  Y* begin() const;
+  Y* end() const;
+};
+
+namespace std {
+template <template <class> class TQual, template <class> class UQual>
+struct basic_common_reference< X, Y, TQual, UQual> {
+  using type = X;
+};
+
+template <template <class> class TQual, template <class> class UQual>
+struct basic_common_reference< Y, X, TQual, UQual> {
+  using type = X;
+};
+} // namespace std
+
+struct R1 : std::ranges::view_base {
+  int* first = nullptr;
+  int* last  = nullptr;
+
+  R1() = default;
+  R1(int* f, int* l) : first(f), last(l) {}
+
+  struct iterator {
+    using value_type       = int;
+    using 
diff erence_type  = std::ptr
diff _t;
+    using iterator_concept = std::forward_iterator_tag;
+
+    int* p = nullptr;
+
+    int& operator*() const { return *p; }
+    iterator& operator++() {
+      ++p;
+      return *this;
+    }
+    iterator operator++(int) {
+      auto tmp = *this;
+      ++*this;
+      return tmp;
+    }
+
+    friend bool operator==(const iterator& x, const iterator& y) { return x.p == y.p; }
+
+    friend X iter_move(const iterator& it) {
+      (void)it;
+      return X{};
+    }
+  };
+
+  iterator begin() const { return iterator{first}; }
+  iterator end() const { return iterator{last}; }
+};
+
+struct R2 : std::ranges::view_base {
+  int* first = nullptr;
+  int* last  = nullptr;
+
+  R2() = default;
+  R2(int* f, int* l) : first(f), last(l) {}
+
+  struct iterator {
+    using value_type       = int;
+    using 
diff erence_type  = std::ptr
diff _t;
+    using iterator_concept = std::forward_iterator_tag;
+
+    int* p = nullptr;
+
+    int& operator*() const { return *p; }
+    iterator& operator++() {
+      ++p;
+      return *this;
+    }
+    iterator operator++(int) {
+      auto tmp = *this;
+      ++*this;
+      return tmp;
+    }
+
+    friend bool operator==(const iterator& x, const iterator& y) { return x.p == y.p; }
+
+    friend Y iter_move(const iterator& it) {
+      (void)it;
+      return Y{};
+    }
+  };
+
+  iterator begin() const { return iterator{first}; }
+  iterator end() const { return iterator{last}; }
+};
+
+struct MoveOnlyIterator {
+  using It = int*;
+
+  It it_;
+
+  using iterator_category = std::input_iterator_tag;
+  using value_type        = int;
+  using 
diff erence_type   = std::ptr
diff _t;
+  using reference         = int&;
+
+  constexpr explicit MoveOnlyIterator(It it) : it_(it) {}
+  MoveOnlyIterator(MoveOnlyIterator&&)                 = default;
+  MoveOnlyIterator& operator=(MoveOnlyIterator&&)      = default;
+  MoveOnlyIterator(const MoveOnlyIterator&)            = delete;
+  MoveOnlyIterator& operator=(const MoveOnlyIterator&) = delete;
+
+  constexpr reference operator*() const { return *it_; }
+
+  constexpr MoveOnlyIterator& operator++() {
+    ++it_;
+    return *this;
+  }
+  constexpr MoveOnlyIterator operator++(int) { return MoveOnlyIterator(it_++); }
+
+  friend constexpr bool operator==(const MoveOnlyIterator& x, const MoveOnlyIterator& y) { return x.it_ == y.it_; }
+  friend constexpr bool operator!=(const MoveOnlyIterator& x, const MoveOnlyIterator& y) { return x.it_ != y.it_; }
+};
+
+struct MoveOnlyView : std::ranges::view_base {
+  int* b;
+  int* e;
+  constexpr MoveOnlyView() = default;
+  constexpr MoveOnlyView(int* b_, int* e_) : b(b_), e(e_) {}
+  MoveOnlyView(const MoveOnlyView&) = delete;
+  constexpr MoveOnlyView(MoveOnlyView&& other) : b(other.b), e(other.e) {}
+  MoveOnlyView& operator=(const MoveOnlyView&) = delete;
+  constexpr MoveOnlyView& operator=(MoveOnlyView&& other) {
+    b = other.b;
+    e = other.e;
+    return *this;
+  }
+
+  constexpr auto begin() const { return MoveOnlyIterator{b}; }
+  constexpr auto end() const { return MoveOnlyIterator{e}; }
+};
+
+template <typename... Ts>
+concept ConcatViewConstraintsPass = requires(Ts&&... a) { std::views::concat(a...); };
+
+int main(int, char**) {
+  // rejects when it is an output range
+  {
+    std::vector<int> v{1, 2, 3};
+    static_assert(!WellFormedView<decltype(std::views::counted(std::back_inserter(v), 3))>);
+  }
+
+  // input range
+  {
+    static_assert(WellFormedView<InputRange>);
+  }
+
+  // bidirectional range
+  {
+    static_assert(WellFormedView<std::list<int>>);
+  }
+
+  // random access range
+  {
+    static_assert(WellFormedView<std::vector<int>>);
+  }
+
+  {
+    // LWG 4082
+    std::vector<int> v{1, 2, 3};
+    auto r = std::views::counted(std::back_inserter(v), 3);
+    //auto c = std::views::concat(r);
+    static_assert(!ConcatViewConstraintsPass<decltype(r)>);
+  }
+
+  {
+    // input is a view and has 0 size
+    static_assert(!ConcatViewConstraintsPass<>);
+  }
+
+  {
+    // input is a view and has at least an element
+    static_assert(ConcatViewConstraintsPass<std::vector<int>>);
+  }
+
+  {
+    // inputs are non-concatable
+    static_assert(!ConcatViewConstraintsPass<std::vector<int>, std::vector<std::string>>);
+  }
+
+  {
+    // test concept concatable
+    {
+      // concat-reference-t is ill-formed
+      // no common reference between int and string
+      static_assert(!ConcatViewConstraintsPass<std::array<int, 1>, std::array<std::string, 1>>);
+    }
+
+    {
+      // concat-value-t is ill-formed but concat-reference-t is valid
+      static_assert(!ConcatViewConstraintsPass<RefOnlyRange1, RefOnlyRange2>);
+    }
+
+    {
+      // concat-rvalue-reference-t is ill-formed
+      static_assert(!ConcatViewConstraintsPass<R1, R2>);
+    }
+
+    {
+      // concat-indirectly-readable is ill-formed
+      static_assert(!ConcatViewConstraintsPass<BadView, BadView>);
+    }
+
+    {
+      // concatable fails when there is a MoveOnly& and MoveOnly
+      // Let Fs be a pack containing MoveOnly& and MoveOnly
+      // common_reference_with<concat_reference_t<Fs...>, concat_value_t<Fs...>> fails
+      static_assert(!ConcatViewConstraintsPass<MoveOnlyView&, MoveOnlyView>);
+    }
+  }
+
+  return 0;
+}

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.concat/ctad.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/ctad.pass.cpp
new file mode 100644
index 0000000000000..900696db9212c
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/ctad.pass.cpp
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// template <class... _Views>
+// concat_view(_Views&&...) -> concat_view<views::all_t<_Views>...>;
+
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+
+#include "test_iterators.h"
+
+struct View : std::ranges::view_base {
+  View() = default;
+  forward_iterator<int*> begin() const;
+  sentinel_wrapper<forward_iterator<int*>> end() const;
+};
+static_assert(std::ranges::view<View>);
+
+// A range that is not a view
+struct Range {
+  Range() = default;
+  forward_iterator<int*> begin() const;
+  sentinel_wrapper<forward_iterator<int*>> end() const;
+};
+static_assert(std::ranges::range<Range> && !std::ranges::view<Range>);
+
+constexpr bool test() {
+  {
+    View v;
+    std::ranges::concat_view view(v);
+    static_assert(std::is_same_v<decltype(view), std::ranges::concat_view<View>>);
+  }
+
+  // Test with a range that isn't a view, to make sure we properly use views::all_t in the implementation.
+  {
+    Range r;
+    std::ranges::concat_view view(r);
+    static_assert(std::is_same_v<decltype(view), std::ranges::concat_view<std::ranges::ref_view<Range>>>);
+  }
+
+  // Test a view which has a range and a view
+  {
+    Range r;
+    View v;
+    std::ranges::concat_view view(r, v);
+    static_assert(std::is_same_v<decltype(view), std::ranges::concat_view<std::ranges::ref_view<Range>, View>>);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.concat/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/ctor.default.pass.cpp
new file mode 100644
index 0000000000000..bdf290cdfe026
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// constexpr concat_view() = default;
+
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+#include <vector>
+
+constexpr int buff[4] = {0, 1, 2, 3};
+
+struct DefaultConstructibleView : std::ranges::view_base {
+  constexpr DefaultConstructibleView() : begin_(buff), end_(buff + 4) {}
+  constexpr int const* begin() const { return begin_; }
+  constexpr int const* end() const { return end_; }
+  constexpr auto size() const { return 4; }
+
+private:
+  int const* begin_;
+  int const* end_;
+};
+
+struct NoDefaultView : std::ranges::view_base {
+  NoDefaultView() = delete;
+  int* begin() const;
+  int* end() const;
+};
+
+using DefaultView = std::ranges::concat_view<DefaultConstructibleView, DefaultConstructibleView>;
+using DefaultViewWithDiffTypes =
+    std::ranges::concat_view<DefaultConstructibleView, decltype(std::views::all(std::declval<std::vector<int>>()))>;
+using BadView1 = std::ranges::concat_view<DefaultConstructibleView, NoDefaultView>;
+using BadView2 = std::ranges::concat_view<NoDefaultView, NoDefaultView>;
+
+constexpr bool test() {
+  static_assert(std::is_default_constructible_v<DefaultView>);
+  static_assert(std::is_default_constructible_v<DefaultViewWithDiffTypes>);
+  static_assert(!std::is_default_constructible_v<BadView1>);
+  static_assert(!std::is_default_constructible_v<BadView2>);
+
+  {
+    DefaultView view = DefaultView();
+    assert(view.size() == 8);
+    auto it = view.begin();
+    assert(*it++ == 0);
+    assert(*it++ == 1);
+    assert(*it++ == 2);
+    assert(*it++ == 3);
+  }
+
+  {
+    DefaultViewWithDiffTypes view = DefaultViewWithDiffTypes();
+    assert(view.size() == 4);
+    auto it = view.begin();
+    assert(*it++ == 0);
+    assert(*it++ == 1);
+    assert(*it++ == 2);
+    assert(*it++ == 3);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.concat/ctor.views.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/ctor.views.pass.cpp
new file mode 100644
index 0000000000000..20afe84bcf980
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/ctor.views.pass.cpp
@@ -0,0 +1,114 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// constexpr explicit concat_view(Views... views)
+
+#include <array>
+#include <cassert>
+#include <ranges>
+
+#include "../range_adaptor_types.h"
+
+template <class T>
+void conversion_test(T);
+
+template <class T, class... Args>
+concept implicitly_constructible_from = requires(Args&&... args) { conversion_test<T>({std::move(args)...}); };
+
+// test constructor is explicit
+static_assert(
+    std::constructible_from<std::ranges::concat_view<SimpleCommon, NonSimpleCommon>, SimpleCommon, NonSimpleCommon>);
+static_assert(!implicitly_constructible_from<std::ranges::concat_view<SimpleCommon, NonSimpleCommon>,
+                                             SimpleCommon,
+                                             NonSimpleCommon>);
+
+struct MoveAwareView : std::ranges::view_base {
+  int moves                 = 0;
+  constexpr MoveAwareView() = default;
+  constexpr MoveAwareView(MoveAwareView&& other) : moves(other.moves + 1) { other.moves = 1; }
+  constexpr MoveAwareView& operator=(MoveAwareView&& other) {
+    moves       = other.moves + 1;
+    other.moves = 0;
+    return *this;
+  }
+  constexpr int* begin() { return &moves; }
+  constexpr int* end() { return &moves + 1; }
+};
+
+constexpr bool test() {
+  int buffer[3]  = {1, 2, 3};
+  int buffer2[2] = {4, 5};
+
+  {
+    // constructor from views
+    std::ranges::concat_view v(SizedRandomAccessView{buffer}, std::array<int, 1>{7});
+    auto it = v.begin();
+    assert(*it == 1);
+    it++;
+    it++;
+    it++;
+    assert(*it == 7);
+  }
+
+  {
+    // arguments are moved
+    MoveAwareView mv;
+    std::ranges::concat_view v{std::move(mv), MoveAwareView{}};
+    auto it = v.begin();
+    assert(*it == 2);
+    it++;
+    assert(*it == 1);
+  }
+
+  {
+    //input and forward range
+    std::ranges::concat_view v(InputCommonView{buffer}, ForwardSizedView{buffer2});
+    auto it = v.begin();
+    assert(*it == 1);
+    it++;
+    assert(*it == 2);
+    it++;
+    assert(*it == 3);
+    it++;
+    assert(*it == 4);
+    it++;
+    assert(*it == 5);
+    it++;
+  }
+
+  {
+    // bidi
+    std::ranges::concat_view v(BidiCommonView{buffer}, SizedBidiCommon{buffer2});
+    auto it = v.begin();
+    assert(*it == 1);
+    it++;
+    assert(*it == 2);
+    it--;
+    assert(*it == 1);
+  }
+
+  {
+    // random access
+    std::ranges::concat_view v(SimpleCommonRandomAccessSized{buffer}, SizedRandomAccessView{buffer2});
+    auto it = v.begin();
+    assert(*it == 1);
+    assert(it[2] == 3);
+    assert(it[3] == 4);
+  }
+
+  return true;
+}
+
+int main() {
+  test();
+  static_assert(test());
+
+  return 0;
+}

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.concat/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/end.pass.cpp
new file mode 100644
index 0000000000000..4d5cd7fbf6412
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/end.pass.cpp
@@ -0,0 +1,116 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// constexpr auto end()
+//   requires(!(__simple_view<_Views> && ...))
+
+// constexpr auto end() const
+//   requires((range<const _Views> && ...) && __concatable<const _Views...>)
+
+#include <array>
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+#include "types.h"
+#include "../range_adaptor_types.h"
+
+constexpr bool test() {
+  int buffer1[5] = {1, 2, 3, 4, 5};
+  int buffer2[2] = {6, 7};
+
+  {
+    std::ranges::concat_view v(SimpleCommon{buffer1}, SimpleCommon{buffer2});
+    static_assert(std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
+    static_assert(std::ranges::common_range<decltype(v)>);
+    assert(v.begin() + 7 == v.end());
+    static_assert(std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
+  }
+
+  {
+    std::ranges::concat_view v(SimpleCommon{buffer1}, SimpleNonCommon{buffer2});
+    assert(v.begin() + 7 == v.end());
+    static_assert(std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
+    static_assert(std::is_same_v<decltype(v.end()), std::default_sentinel_t>);
+  }
+
+  {
+    std::ranges::concat_view v(SimpleCommon{buffer1}, NonSimpleCommon{buffer2});
+    assert(v.begin() + 7 == v.end());
+    static_assert(!std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
+  }
+
+  {
+    std::ranges::concat_view v(SimpleCommon{buffer1}, NonSimpleNonCommon{buffer2});
+    assert(v.begin() + 7 == v.end());
+    static_assert(std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
+    static_assert(std::is_same_v<decltype(v.end()), std::default_sentinel_t>);
+  }
+
+  {
+    // all the ranges but the last one are input ranges, the last range is common => end() returns sentinel
+    // https://cplusplus.github.io/LWG/issue4166
+
+    using Iter      = cpp20_input_iterator<const int*>;
+    using Sentinel  = sentinel_wrapper<Iter>;
+    using InputView = minimal_view<Iter, Sentinel>;
+
+    std::array<int, 3> arr{1, 2, 3};
+    auto v = std::views::concat(InputView{Iter{buffer1}, Sentinel{Iter{buffer1 + 5}}}, arr);
+    static_assert(std::same_as<decltype(v.end()), std::default_sentinel_t>);
+    auto it = v.begin();
+    it++;
+    it++;
+    it++;
+    it++;
+    it++;
+    it++;
+    it++;
+    it++;
+    assert(it == v.end());
+  }
+
+  {
+    // first range is forward range, the last range is common => end() does not return sentinel
+    using Iter        = forward_iterator<const int*>;
+    using Sentinel    = sentinel_wrapper<Iter>;
+    using ForwardView = minimal_view<Iter, Sentinel>;
+
+    std::array<int, 2> arr{6, 7};
+    auto v = std::views::concat(ForwardView{Iter{buffer1}, Sentinel{Iter{buffer1 + 5}}}, arr);
+    static_assert(!std::same_as<decltype(v.end()), std::default_sentinel_t>);
+    static_assert(std::same_as<decltype(v.end()), decltype(v.begin())>);
+
+    auto it   = v.begin();
+    auto last = v.end();
+    int sum   = 0;
+    for (; it != last; it++) {
+      sum += *it;
+    }
+    assert(sum == 1 + 2 + 3 + 4 + 5 + 6 + 7);
+  }
+
+  {
+    // testing concatable constraint
+    static_assert(!ConcatableConstViews<ViewWithNoConstBegin>);
+    static_assert(ConcatableConstViews<ViewWithConstBegin>);
+    static_assert(!ConcatableConstViews<ViewWithNoConstBegin, ViewWithConstBegin>);
+    static_assert(!ConcatableConstViews<ViewWithNoConstBegin, ViewWithConstBegin, SizedViewWithConstBegin>);
+    static_assert(ConcatableConstViews<ViewWithConstBegin, SizedViewWithConstBegin>);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/arithmetic.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/arithmetic.pass.cpp
new file mode 100644
index 0000000000000..401de04bf34aa
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/arithmetic.pass.cpp
@@ -0,0 +1,655 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// operator-(x, y)
+// operator-(x, default_sentinel )
+// operator-(default_sentinel , x)
+// operator-(x, n)
+// operator+(x, n)
+// operator+=(x, n)
+// operator-=(x, n)
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <functional>
+#include <list>
+#include <ranges>
+#include <span>
+#include <vector>
+#include <utility>
+
+#include "../../range_adaptor_types.h"
+
+template <class T, class U>
+concept canPlusEqual = requires(T& t, U& u) { t += u; };
+
+template <class T, class U>
+concept canMinusEqual = requires(T& t, U& u) { t -= u; };
+
+template <class Iter>
+struct NotSizedSentinelForIter {
+  using iterator_category = std::forward_iterator_tag;
+  using value_type        = std::iterator_traits<Iter>::value_type;
+  using 
diff erence_type   = std::iterator_traits<Iter>::
diff erence_type;
+
+  Iter ptr;
+
+  NotSizedSentinelForIter() = default;
+  NotSizedSentinelForIter(const Iter& p) : ptr(p) {}
+  NotSizedSentinelForIter(const NotSizedSentinelForIter& other) : ptr(other.ptr) {}
+
+  value_type& operator*() const { return *ptr; }
+
+  NotSizedSentinelForIter& operator++() { return *++ptr; }
+  NotSizedSentinelForIter operator++(int) {
+    auto tmp = *this;
+    ++*this;
+    return tmp;
+  }
+
+  friend bool operator==(const NotSizedSentinelForIter& a, const NotSizedSentinelForIter& b) = default;
+};
+
+template <class T>
+struct NotSizedViewWithSizedSentinel;
+
+template <class Iter>
+struct SizedSentinelForIter {
+  using iterator_category = std::forward_iterator_tag;
+  using value_type        = std::iterator_traits<Iter>::value_type;
+  using 
diff erence_type   = std::iterator_traits<Iter>::
diff erence_type;
+
+  Iter ptr;
+  Iter e;
+
+  constexpr SizedSentinelForIter() = default;
+  constexpr SizedSentinelForIter(const Iter& ptr_, const Iter& e_ = nullptr) : ptr(ptr_), e(e_) {}
+  constexpr SizedSentinelForIter(const SizedSentinelForIter& other) : ptr(other.ptr), e(other.e) {}
+
+  constexpr value_type& operator*() const { return *ptr; }
+
+  constexpr SizedSentinelForIter& operator++() {
+    ++ptr;
+    return *this;
+  }
+  constexpr SizedSentinelForIter operator++(int) {
+    auto tmp = *this;
+    ++*this;
+    return tmp;
+  }
+
+  friend constexpr decltype(auto) operator-(const SizedSentinelForIter& a, const SizedSentinelForIter& b) {
+    return a.ptr - b.ptr;
+  }
+
+  friend constexpr bool operator==(const SizedSentinelForIter& a, const SizedSentinelForIter& b) = default;
+};
+
+template <class T>
+struct SizedViewWithoutSizedSentinel : std::ranges::view_base {
+  T* b_;
+  std::size_t sz;
+
+  template <std::size_t N>
+  constexpr SizedViewWithoutSizedSentinel(T (&b)[N]) : b_(b), sz(N) {}
+  constexpr SizedViewWithoutSizedSentinel(T* b, std::size_t N) : b_(b), sz(N) {}
+
+  constexpr NotSizedSentinelForIter<T*> begin() const { return NotSizedSentinelForIter<T*>{b_}; }
+  constexpr NotSizedSentinelForIter<T*> end() const { return NotSizedSentinelForIter<T*>{b_ + sz}; }
+
+  constexpr std::size_t size() { return sz; }
+};
+
+template <class T>
+struct NotSizedViewWithSizedSentinel : std::ranges::view_base {
+  T* b_;
+  std::size_t sz;
+
+  NotSizedViewWithSizedSentinel() = default;
+
+  template <std::size_t N>
+  constexpr NotSizedViewWithSizedSentinel(T (&b)[N]) : b_(b), sz(N) {}
+  constexpr NotSizedViewWithSizedSentinel(T* b, std::size_t N) : b_(b), sz(N) {}
+
+  constexpr SizedSentinelForIter<T*> begin() const { return SizedSentinelForIter<T*>{b_, b_ + sz}; }
+  constexpr SizedSentinelForIter<T*> end() const { return SizedSentinelForIter<T*>{b_ + sz, b_ + sz}; }
+};
+
+static_assert(std::forward_iterator<NotSizedSentinelForIter<int*>>);
+static_assert(std::sized_sentinel_for<std::ranges::sentinel_t<NotSizedViewWithSizedSentinel<int>>,
+                                      std::ranges::iterator_t<NotSizedViewWithSizedSentinel<int>>>);
+
+template <class... Views>
+concept MinusOperatorWellFormedForDefaultSentinel =
+    requires(std::ranges::concat_view<Views...> cv) { (cv.begin() - std::default_sentinel_t{}); };
+
+constexpr bool test() {
+  int buffer1[5] = {1, 2, 3, 4, 5};
+  int buffer2[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+  int buffer3[4] = {4, 5, 6, 7};
+
+  SimpleCommonRandomAccessSized a{buffer1};
+  SimpleCommonRandomAccessSized b{buffer2};
+  SimpleCommonRandomAccessSized c{buffer3};
+
+  {
+    // operator+(x, n) and operator+=
+    std::ranges::concat_view v(a, b);
+    auto it1 = v.begin();
+
+    auto it2  = it1 + 3;
+    auto x2   = *it2;
+    it2       = it2 + (-2);
+    auto x2_1 = *it2;
+    assert(x2 == buffer1[3]);
+    assert(x2_1 == buffer1[1]);
+
+    auto it3  = 3 + it1;
+    auto x3   = *it3;
+    it3       = (-2) + it3;
+    auto x3_1 = *it3;
+    assert(x3 == buffer1[3]);
+    assert(x3_1 == buffer1[1]);
+
+    it1 += 3;
+    auto x1 = *it1;
+    it1 += (-2);
+    auto x1_1 = *it1;
+    assert(x1 == buffer1[3]);
+    assert(x1_1 == buffer1[1]);
+
+    //  reaches the end of one range so it should jump to the beginning of next range
+    auto it = v.begin() + 5;
+    assert(*it == buffer2[0]);
+
+    using Iter = decltype(it1);
+    static_assert(canPlusEqual<Iter, std::intptr_t>);
+  }
+
+  {
+    // operator+(x, n) and operator+=
+    // has empty view
+    std::array<int, 0> e{};
+    std::ranges::concat_view v(a, e, b);
+    auto it1 = v.begin();
+
+    auto it2  = it1 + 3;
+    auto x2   = *it2;
+    it2       = it2 + (-2);
+    auto x2_1 = *it2;
+    assert(x2 == buffer1[3]);
+    assert(x2_1 == buffer1[1]);
+
+    auto it3  = 3 + it1;
+    auto x3   = *it3;
+    it3       = (-2) + it3;
+    auto x3_1 = *it3;
+    assert(x3 == buffer1[3]);
+    assert(x3_1 == buffer1[1]);
+
+    it1 += 3;
+    auto x1 = *it1;
+    it1 += (-2);
+    auto x1_1 = *it1;
+    assert(x1 == buffer1[3]);
+    assert(x1_1 == buffer1[1]);
+
+    // jumps over to next range but next range is empty, so it should go futher
+    auto it = v.begin() + 5;
+    assert(*it == buffer2[0]);
+
+    using Iter = decltype(it1);
+    static_assert(canPlusEqual<Iter, std::intptr_t>);
+  }
+
+  {
+    // operator+(x, n) and operator+=
+    // has more than 2 ranges
+    std::array<int, 0> e{};
+    std::ranges::concat_view v(a, e, b, c);
+    auto it1 = v.begin();
+
+    auto it2  = it1 + 3;
+    auto x2   = *it2;
+    it2       = it2 + (-2);
+    auto x2_1 = *it2;
+    assert(x2 == buffer1[3]);
+    assert(x2_1 == buffer1[1]);
+
+    auto it3  = 3 + it1;
+    auto x3   = *it3;
+    it3       = (-2) + it3;
+    auto x3_1 = *it3;
+    assert(x3 == buffer1[3]);
+    assert(x3_1 == buffer1[1]);
+
+    it1 += 3;
+    auto x1 = *it1;
+    it1 += (-2);
+    auto x1_1 = *it1;
+    assert(x1 == buffer1[3]);
+    assert(x1_1 == buffer1[1]);
+    // jumps over to next range but next range is empty, so it should go futher
+    auto it = v.begin() + 5;
+    assert(*it == buffer2[0]);
+
+    // jumps big enough to skip several number of ranges
+    it       = v.begin() + 14;
+    auto x   = *it;
+    it       = it + (-3);
+    auto x_1 = *it;
+    assert(x == buffer3[0]);
+    assert(x_1 == buffer2[6]);
+
+    using Iter = decltype(it1);
+    static_assert(canPlusEqual<Iter, std::intptr_t>);
+  }
+
+  {
+    // operator-(x, n) and operator-=
+    std::ranges::concat_view v(a, b);
+    auto it1 = v.end();
+
+    auto it2  = it1 - 3;
+    auto x2   = *it2;
+    it2       = it2 - (-1);
+    auto x2_1 = *it2;
+    assert(x2 == buffer2[6]);
+    assert(x2_1 == buffer2[7]);
+
+    it1 -= 2;
+    assert(it1 == it2);
+    auto x1 = *it1;
+    it1 -= (-1);
+    auto x1_1 = *it1;
+    assert(x1 == buffer2[7]);
+    assert(x1_1 == buffer2[8]);
+
+    // move back to one past the begining,
+    // so it should point to the elements of the previous range
+
+    auto it = v.end() - 12;
+    auto x  = *it;
+    it -= (-1);
+    auto x_1 = *it;
+    assert(x == buffer1[2]);
+    assert(x_1 == buffer1[3]);
+
+    using Iter = decltype(it1);
+    static_assert(canMinusEqual<Iter, std::intptr_t>);
+  }
+
+  {
+    // operator-(x, n) and operator-=
+    // has empty view
+    std::array<int, 0> e{};
+    std::ranges::concat_view v(a, e, b);
+    auto it1 = v.end();
+
+    auto it2  = it1 - 3;
+    auto x2   = *it2;
+    it2       = it2 - (-1);
+    auto x2_1 = *it2;
+    assert(x2 == buffer2[6]);
+    assert(x2_1 == buffer2[7]);
+
+    it1 -= 2;
+    assert(it1 == it2);
+    auto x1 = *it1;
+    it1 -= (-1);
+    auto x1_1 = *it1;
+    assert(x1 == buffer2[7]);
+    assert(x1_1 == buffer2[8]);
+
+    // move back to an empty range
+    // so it should skip empty range point to the elements of the previous range
+
+    auto it = v.end() - 12;
+    auto x  = *it;
+    it -= (-1);
+    auto x_1 = *it;
+    assert(x == buffer1[2]);
+    assert(x_1 == buffer1[3]);
+
+    using Iter = decltype(it1);
+    static_assert(canMinusEqual<Iter, std::intptr_t>);
+  }
+
+  {
+    // operator-(x, n) and operator-=
+    // has more than 2 views
+    std::array<int, 0> e{};
+    std::ranges::concat_view v(a, e, b, c);
+    auto it1 = v.end();
+
+    auto it2 = it1 - 3;
+    auto x2  = *it2;
+    it2 -= (-1);
+    auto x2_1 = *it2;
+    assert(x2 == buffer3[1]);
+    assert(x2_1 == buffer3[2]);
+
+    it1 -= 2;
+    assert(it1 == it2);
+    auto x1 = *it1;
+    it1 -= (-1);
+    auto x1_1 = *it1;
+    assert(x1 == buffer3[2]);
+    assert(x1_1 == buffer3[3]);
+
+    // move back to multiple ranges
+
+    auto it  = v.end() - 16;
+    auto x   = *it;
+    it       = it - (-1);
+    auto x_1 = *it;
+    it -= (-1);
+    auto x_2 = *it;
+    assert(x == buffer1[2]);
+    assert(x_1 == buffer1[3]);
+    assert(x_2 == buffer1[4]);
+
+    using Iter = decltype(it1);
+    static_assert(canMinusEqual<Iter, std::intptr_t>);
+  }
+
+  {
+    // operator-(x, y)
+    // x and y are in 
diff erent ranges
+    // underlying ranges are the same
+    // x'index < y's index
+    std::ranges::concat_view v(a, b);
+    assert((v.end() - v.begin()) == 14);
+
+    // x'index < y's index
+    auto it1 = v.begin() + 2;
+    auto it2 = v.end() - 1;
+    assert((it1 - it2) == -11);
+
+    // x'index > y's index
+    assert((it2 - it1) == 11);
+
+    // x'index == y's index
+    it1 = it1 + 11;
+    assert((it2 - it1) == 0);
+  }
+
+  {
+    // opeartor-(x,y)
+    // x and y are in 
diff erent ranges
+    // underlying ranges are 
diff erent types
+    std::array<int, 3> arr_a{1, 2, 3};
+    std::vector<int> arr_b{4, 5, 6};
+    std::ranges::concat_view v(arr_a, arr_b);
+
+    // x'index < y's index
+    auto it1 = v.begin() + 1;
+    auto it2 = v.end() - 1;
+    assert(*it1 == 2);
+    assert(*it2 == 6);
+    assert((it1 - it2) == -4);
+
+    // x'index > y's index
+    assert((it2 - it1) == 4);
+
+    // x'index == y's index
+    it1 = it1 + 4;
+    assert((it2 - it1) == 0);
+  }
+
+  {
+    // opeartor-(x,y)
+    // x and y are in 
diff erent ranges
+    // underlying ranges are 
diff erent types, and there are empty ranges in the middle
+    std::array<int, 0> e_1{};
+    std::array<int, 0> e_2{};
+    std::vector<int> arr_b{4, 5, 6};
+    std::ranges::concat_view v(a, e_1, e_2, arr_b);
+
+    // x'index < y's index
+    auto it1 = v.begin() + 1;
+    auto it2 = v.end() - 1;
+    assert(*it1 == 2);
+    assert(*it2 == 6);
+    assert((it1 - it2) == -6);
+
+    // x'index > y's index
+    assert((it2 - it1) == 6);
+
+    // x'index == y's index
+    it1 = it1 + 6;
+    assert((it2 - it1) == 0);
+  }
+
+  {
+    // operator-(default_sentinel , x)
+    std::array<int, 2> array1{0, 1};
+    std::array<int, 2> array2{2, 3};
+    std::array<int, 2> array3{4, 5};
+    std::ranges::concat_view view(std::views::all(array1), std::views::all(array2), std::views::all(array3));
+    auto it1 = view.begin();
+    auto res = std::default_sentinel_t{} - it1;
+    assert(res == 6);
+  }
+
+  {
+    // operator-(default_sentinel , x) with empty ranges
+    std::array<int, 2> array1{0, 1};
+    std::array<int, 0> array2;
+    std::array<int, 2> array3{2, 3};
+    std::ranges::concat_view view(std::views::all(array1), std::views::all(array2), std::views::all(array3));
+    auto it1 = view.begin();
+    auto res = std::default_sentinel_t{} - it1;
+    assert(res == 4);
+  }
+
+  {
+    // operator-(default_sentinel , x) with 
diff erent types
+    std::array<int, 2> array1{0, 1};
+    std::vector<int> array2{2, 3};
+    std::array<int, 0> array3{};
+    std::ranges::concat_view view(a, std::views::all(array1), std::views::all(array2), std::views::all(array3));
+    auto it1 = view.begin();
+    auto res = std::default_sentinel_t{} - it1;
+    assert(res == 9);
+  }
+
+  {
+    // operator-(x, default_sentinel)
+    std::array<int, 2> array1{0, 1};
+    std::array<int, 2> array2{2, 3};
+    std::array<int, 2> array3{4, 5};
+    std::ranges::concat_view view(std::views::all(array1), std::views::all(array2), std::views::all(array3));
+    auto it1 = view.begin();
+    auto res = it1 - std::default_sentinel_t{};
+    assert(res == -6);
+  }
+
+  {
+    // operator-(x, default_sentinel) with empty ranges
+    std::array<int, 2> array1{0, 1};
+    std::array<int, 0> array2{};
+    std::array<int, 2> array3{4, 5};
+    std::ranges::concat_view view(std::views::all(array1), std::views::all(array2), std::views::all(array3));
+    auto it1 = view.begin();
+    auto res = it1 - std::default_sentinel_t{};
+    assert(res == -4);
+  }
+
+  {
+    // operator-(x, default_sentinel) with 
diff erent types
+    std::array<int, 2> array1{0, 1};
+    std::array<int, 0> array2{};
+    std::vector<int> array3{4, 5};
+    std::ranges::concat_view view(std::views::all(array1), std::views::all(array2), std::views::all(array3));
+    auto it1 = view.begin();
+    auto res = it1 - std::default_sentinel_t{};
+    assert(res == -4);
+  }
+
+  {
+    // operator-(x, default_sentinel)
+    // testing constraints
+
+    // sized_sentinel_for fails
+    static_assert(!MinusOperatorWellFormedForDefaultSentinel<SizedViewWithoutSizedSentinel<int>>);
+    static_assert(!MinusOperatorWellFormedForDefaultSentinel<SizedViewWithoutSizedSentinel<int>,
+                                                             NonSimpleCommonRandomAccessSized>);
+    static_assert(
+        MinusOperatorWellFormedForDefaultSentinel<SimpleCommonRandomAccessSized, NonSimpleCommonRandomAccessSized>);
+
+    // let Fs be the pack containing all views but the first one
+    // sized_sentinel_for succeeds and sized_range<Fs...> succeeds
+    // first range does not have size() but satisfies sized_sentinel_for
+    int arr_a[3] = {0, 1, 2};
+    int arr_b[3] = {4, 5, 6};
+    static_assert(
+        MinusOperatorWellFormedForDefaultSentinel<NotSizedViewWithSizedSentinel<int>, SimpleCommonRandomAccessSized>);
+    std::ranges::concat_view cv{NotSizedViewWithSizedSentinel<int>{arr_a}, SimpleCommonRandomAccessSized{arr_b}};
+    auto it = cv.begin();
+    it++;
+    assert((it - std::default_sentinel_t{}) == -5);
+    assert((std::default_sentinel_t{} - it) == 5);
+
+    // sized_sentinel_for succeeds but sized_range<Fs...> fails
+    static_assert(!MinusOperatorWellFormedForDefaultSentinel<NotSizedViewWithSizedSentinel<int>, InputCommonView>);
+    static_assert(!MinusOperatorWellFormedForDefaultSentinel<SimpleCommonRandomAccessSized, InputCommonView>);
+  }
+
+  {
+    // One of the ranges is not random access
+    std::ranges::concat_view v(a, b, ForwardSizedView{buffer1});
+    using Iter = decltype(v.begin());
+    static_assert(!std::invocable<std::plus<>, Iter, std::intptr_t>);
+    static_assert(!std::invocable<std::plus<>, std::intptr_t, Iter>);
+    static_assert(!canPlusEqual<Iter, std::intptr_t>);
+    static_assert(!std::invocable<std::minus<>, Iter, std::intptr_t>);
+    static_assert(!std::invocable<std::minus<>, Iter, Iter>);
+    static_assert(!canMinusEqual<Iter, std::intptr_t>);
+  }
+
+  {
+    // One of the ranges is not random access
+    std::ranges::concat_view v(a, b, InputCommonView{buffer1});
+    using Iter = decltype(v.begin());
+    static_assert(!std::invocable<std::minus<>, Iter, Iter>);
+  }
+
+  {
+    // random access check
+    std::array<int, 4> a1{1, 2, 3, 4};
+    std::array<int, 2> b1{5, 6};
+    std::span<const int> s1{a1};
+    std::span<const int> s2{b1};
+
+    // All random-access & all non-last are common => random access iterator
+    {
+      auto v      = std::views::concat(s1, s2); // both spans are RA & common; non-last (s1) is common
+      using Iter  = decltype(v.begin());
+      using CIter = decltype(std::as_const(v).begin());
+      static_assert(std::random_access_iterator<Iter>);
+      static_assert(std::random_access_iterator<CIter>);
+    }
+
+    // Others are common and last is  be non-common => still random access
+    {
+      auto last_non_common = std::views::counted(a1.data(), static_cast<std::ptr
diff _t>(a1.size()));
+      auto v               = std::views::concat(s2, last_non_common); // s2 is common; last is allowed to be non-common
+      using Iter           = decltype(v.begin());
+      using CIter          = decltype(std::as_const(v).begin());
+      static_assert(std::random_access_iterator<Iter>);
+      static_assert(std::random_access_iterator<CIter>);
+    }
+
+    // a non-last range is non-common => NOT random access
+    {
+      int buffer[3] = {1, 2, 3};
+      auto v        = std::views::concat(SimpleNonCommon{buffer}, s2);
+      using Iter    = decltype(v.begin());
+      using CIter   = decltype(std::as_const(v).begin());
+      static_assert(!std::random_access_iterator<Iter>);
+      static_assert(!std::random_access_iterator<CIter>);
+    }
+
+    // one underlying range is not random access => NOT random access
+    {
+      std::list<int> ls{1, 2, 3};
+      auto v      = std::views::concat(ls, s2);
+      using Iter  = decltype(v.begin());
+      using CIter = decltype(std::as_const(v).begin());
+      static_assert(!std::random_access_iterator<Iter>);
+      static_assert(!std::random_access_iterator<CIter>);
+    }
+  }
+
+  {
+    // operator+(x, n) and operator+(n, x), where n is negative
+    std::array<int, 4> arr_a{1, 2, 3, 4};
+    std::array<int, 3> arr_b{5, 6, 7};
+    std::span<const int> s1{arr_a};
+    std::span<const int> s2{arr_b};
+    auto v = std::views::concat(s1, s2);
+
+    auto i = v.begin();
+    std::ranges::advance(i, arr_a.size());
+
+    auto j   = i;
+    auto j_1 = j + (-1);
+    assert(*j_1 == arr_a.back());
+    auto j_3 = j + (-3);
+    assert(*j_3 == arr_a[1]);
+
+    // n + x (negative)
+    auto k = (-1) + j;
+    assert(*k == arr_a.back());
+
+    // const-iterator
+    auto ci = std::as_const(v).begin();
+    std::ranges::advance(ci, arr_a.size());
+    auto cj   = ci;
+    auto cj_2 = cj + (-2);
+    assert(*cj_2 == arr_a[2]);
+    auto cjn = (-1) + cj;
+    assert(*cjn == arr_a.back());
+  }
+
+  {
+    // operator-(x, n), where n is negative
+    std::array<int, 4> arr_a{1, 2, 3, 4};
+    std::array<int, 3> arr_b{5, 6, 7};
+    std::span<const int> s1{arr_a};
+    std::span<const int> s2{arr_b};
+    auto v = std::views::concat(s1, s2);
+
+    auto i = v.begin();
+
+    auto j   = i;
+    auto j_1 = j - (-1);
+    assert(*j_1 == arr_a[1]);
+    auto j_3 = j - (-3);
+    assert(*j_3 == arr_a.back());
+
+    // const-iterator
+    auto ci   = std::as_const(v).begin();
+    auto cj   = ci;
+    auto cj_2 = cj - (-2);
+    assert(*cj_2 == arr_a[2]);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/compare.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/compare.pass.cpp
new file mode 100644
index 0000000000000..ba61923ee955f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/compare.pass.cpp
@@ -0,0 +1,236 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// operator==(x,y)
+// operator==(x, sentinel)
+// operator<(x, y)
+// operator<=(x, y)
+// operator>=(x, y)
+// operator>(x, y)
+// operator<=>(x, y)
+
+#include <array>
+#include <cassert>
+#include <ranges>
+#include <vector>
+
+#include "../../range_adaptor_types.h"
+
+template <class Iter>
+concept canCompareEqual = requires(Iter a, Iter b) { a == b; };
+
+template <class Iter>
+concept canCompareEqualWithDefaultSentinel = requires(Iter a, std::default_sentinel_t b) { a == b; };
+
+template <class Iter>
+concept canCompareLessthan = requires(Iter a, Iter b) { a < b; };
+
+template <class Iter>
+concept canCompareLessthanOrEqual = requires(Iter a, Iter b) { a <= b; };
+
+template <class Iter>
+concept canCompareGreaterThan = requires(Iter a, Iter b) { a > b; };
+
+template <class Iter>
+concept canCompareGreaterThanOrEqual = requires(Iter a, Iter b) { a >= b; };
+
+template <class Iter>
+concept canCompareThreeWay = requires(Iter a, Iter b) { a <=> b; };
+
+constexpr bool test() {
+  {
+    // test with one view
+    std::array<int, 5> array{0, 1, 2, 3, 4};
+    std::ranges::concat_view view((array));
+    assert(!(view.begin() == view.end()));
+    assert(view.begin() != view.end());
+    decltype(auto) it1                       = view.begin();
+    decltype(auto) it2                       = view.begin();
+    std::same_as<bool> decltype(auto) result = (it1 == it2);
+    assert(result);
+
+    ++it1;
+    assert(!(it1 == it2));
+    assert(!(it2 == it1));
+  }
+
+  {
+    // test with more than one view
+    std::array<int, 3> array1{0, 1, 2};
+    std::vector<int> array2{0, 1, 2};
+    std::ranges::concat_view view(std::views::all(array1), std::views::all(array2));
+    decltype(auto) it1                       = view.begin();
+    decltype(auto) it2                       = view.begin();
+    std::same_as<bool> decltype(auto) result = (it1 == it2);
+    assert(result);
+
+    ++it2;
+    ++it2;
+    assert(!(it1 == it2));
+    assert(!(it2 == it1));
+    assert(it2 != it1);
+    assert(it1 != it2);
+    ++it2;
+    assert(*it1 == *it2);
+    assert(*it2 == *it1);
+    assert(!(*it1 != *it2));
+    assert(!(*it2 != *it1));
+  }
+
+  {
+    // test with more than one view and iterators are in 
diff erent range
+    std::array<int, 3> array1{0, 1, 2};
+    std::vector<int> array2{4, 5, 6};
+    std::ranges::concat_view view(std::views::all(array1), std::views::all(array2));
+    decltype(auto) it1 = view.begin();
+    decltype(auto) it2 = view.begin() + 3;
+
+    assert(it1 != it2);
+    assert(it2 != it1);
+    assert(!(it1 == it2));
+    assert(!(it2 == it1));
+    assert(*it1 == 0);
+    assert(*it2 == 4);
+    it1++;
+    it2++;
+    assert(*it1 == 1);
+    assert(*it2 == 5);
+  }
+
+  {
+    // operator==(x, sentinel)
+    std::array<int, 2> array1{1, 2};
+    std::vector<int> array2{3, 4};
+    std::ranges::concat_view v(std::views::all(array1), std::views::all(array2));
+
+    auto it = v.begin();
+    assert(!(it == std::default_sentinel_t{}));
+    assert(!(std::default_sentinel_t{} == it));
+    assert(it != std::default_sentinel_t{});
+    assert(std::default_sentinel_t{} != it);
+
+    it++;
+    it++;
+    it++;
+    it++;
+    assert(it == std::default_sentinel_t{});
+    assert(std::default_sentinel_t{} == it);
+    assert(!(it != std::default_sentinel_t{}));
+    assert(!(std::default_sentinel_t{} != it));
+
+    // const-iterator
+    const auto& cv = v;
+    auto cit       = cv.begin();
+    ++cit;
+    ++cit;
+    ++cit;
+    ++cit;
+    assert(cit == std::default_sentinel_t{});
+    assert(std::default_sentinel_t{} == cit);
+    assert(!(cit != std::default_sentinel_t{}));
+    assert(!(std::default_sentinel_t{} != cit));
+  }
+
+  {
+    // operator <, <=, >, >=
+    std::array<int, 4> arr_a{1, 2, 3, 4};
+    std::vector<int> arr_b{5, 6, 7};
+    auto v = std::views::concat(arr_a, arr_b);
+    auto i = v.begin();
+    auto j = v.begin();
+    std::ranges::advance(j, arr_a.size());
+
+    assert(i < j);
+    assert(i <= j);
+    assert(!(i > j));
+    assert(!(i >= j));
+    assert((i <=> j) == std::strong_ordering::less);
+    assert((i <=> i) == std::strong_ordering::equal);
+    assert((j <=> i) == std::strong_ordering::greater);
+
+    auto k = j;
+    assert(!(j < k));
+    assert(j <= k);
+    assert(!(j > k));
+    assert(j >= k);
+    auto ord2 = (j <=> k);
+    assert(ord2 == 0);
+
+    // const-iterator
+    const auto& cv = v;
+    auto ci        = cv.begin();
+    auto cj        = cv.begin();
+    std::ranges::advance(cj, arr_a.size());
+    assert(ci < cj);
+    assert((ci <=> cj) < 0);
+  }
+
+  {
+    // operator <, <=, >, >=
+    // two pointers point to elements in the same range
+    std::array<int, 4> arr_a{1, 2, 3, 4};
+    std::vector<int> arr_b{5, 6, 7};
+    auto v = std::views::concat(arr_a, arr_b);
+    auto i = v.begin();
+    auto j = v.begin() + 2;
+
+    assert(i < j);
+    assert(i <= j);
+    assert(!(i > j));
+    assert(!(i >= j));
+    assert((i <=> j) == std::strong_ordering::less);
+    assert((i <=> i) == std::strong_ordering::equal);
+    assert((j <=> i) == std::strong_ordering::greater);
+
+    auto k = j;
+    assert(!(j < k));
+    assert(j <= k);
+    assert(!(j > k));
+    assert(j >= k);
+    auto ord2 = (j <=> k);
+    assert(ord2 == 0);
+
+    // const-iterator
+    const auto& cv = v;
+    auto ci        = cv.begin();
+    auto cj        = cv.begin();
+    std::ranges::advance(cj, arr_a.size());
+    assert(ci < cj);
+    assert((ci <=> cj) < 0);
+  }
+
+  {
+    // operator ==,<, <=, >, >=, <=>
+    // should not be invocable on non-random access range
+    static_assert(!canCompareEqual<std::ranges::concat_view<ForwardSizedView>>);
+    static_assert(!canCompareEqualWithDefaultSentinel<std::ranges::concat_view<ForwardSizedView>>);
+    static_assert(!canCompareLessthan<std::ranges::concat_view<ForwardSizedView>>);
+    static_assert(!canCompareLessthanOrEqual<std::ranges::concat_view<ForwardSizedView>>);
+    static_assert(!canCompareGreaterThan<std::ranges::concat_view<ForwardSizedView>>);
+    static_assert(!canCompareGreaterThanOrEqual<std::ranges::concat_view<ForwardSizedView>>);
+    static_assert(!canCompareThreeWay<std::ranges::concat_view<ForwardSizedView>>);
+
+    static_assert(!canCompareEqual<std::ranges::concat_view<ForwardSizedView, SizedBidiCommon>>);
+    static_assert(!canCompareEqualWithDefaultSentinel<std::ranges::concat_view<ForwardSizedView, SizedBidiCommon>>);
+    static_assert(!canCompareLessthan<std::ranges::concat_view<ForwardSizedView, SizedBidiCommon>>);
+    static_assert(!canCompareLessthanOrEqual<std::ranges::concat_view<ForwardSizedView, SizedBidiCommon>>);
+    static_assert(!canCompareGreaterThan<std::ranges::concat_view<ForwardSizedView, SizedBidiCommon>>);
+    static_assert(!canCompareGreaterThanOrEqual<std::ranges::concat_view<ForwardSizedView, SizedBidiCommon>>);
+    static_assert(!canCompareThreeWay<std::ranges::concat_view<ForwardSizedView, SizedBidiCommon>>);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/ctor.default.pass.cpp
new file mode 100644
index 0000000000000..f27fe3a14076c
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/ctor.default.pass.cpp
@@ -0,0 +1,49 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: has-fblocks
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// constexpr iterator() = default;
+
+#include <ranges>
+#include <type_traits>
+
+#include "test_macros.h"
+
+struct IntView : std::ranges::view_base {
+  int* b_ = nullptr;
+  int* e_ = nullptr;
+
+  constexpr IntView() = default;
+  constexpr IntView(int* b, int* e) : b_(b), e_(e) {}
+
+  constexpr int* begin() const { return b_; }
+  constexpr int* end() const { return e_; }
+};
+
+static_assert(std::ranges::view<IntView>);
+static_assert(std::ranges::contiguous_range<IntView>);
+
+constexpr bool test() {
+  int buf1[] = {1, 2};
+  int buf2[] = {3, 4};
+
+  std::ranges::concat_view<IntView, IntView> v(IntView{buf1, buf1 + 2}, IntView{buf2, buf2 + 2});
+  using Iter = std::ranges::iterator_t<decltype(v)>;
+  static_assert(std::default_initializable<Iter>);
+  [[maybe_unused]] Iter iter{};
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/ctor.other.pass.cpp
new file mode 100644
index 0000000000000..34ec64f8af8ec
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/ctor.other.pass.cpp
@@ -0,0 +1,84 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// constexpr __iterator(__iterator<!_Const> __i)
+//     requires _Const && (convertible_to<iterator_t<_Views>, iterator_t<const _Views>> && ...)
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+
+#include "../../range_adaptor_types.h"
+
+using ConstIterIncompatibleView =
+    BasicView<forward_iterator<int*>,
+              forward_iterator<int*>,
+              random_access_iterator<const int*>,
+              random_access_iterator<const int*>>;
+static_assert(!std::convertible_to<std::ranges::iterator_t<ConstIterIncompatibleView>,
+                                   std::ranges::iterator_t<const ConstIterIncompatibleView>>);
+
+constexpr bool test() {
+  int buffer_1[3] = {1, 2, 3};
+  int buffer_2[3] = {4, 5, 6};
+
+  {
+    std::ranges::concat_view v(NonSimpleCommon{buffer_1}, NonSimpleCommonRandomAccessSized{buffer_2});
+    auto iter1 = v.begin();
+    iter1++;
+    std::ranges::iterator_t<const decltype(v)> iter2 = iter1;
+    assert(iter1 == iter2);
+    assert(*iter1 == 2);
+    assert(*iter2 == 2);
+
+    static_assert(!std::is_same_v<decltype(iter1), decltype(iter2)>);
+
+    // We cannot create a non-const iterator from a const iterator.
+    static_assert(!std::constructible_from<decltype(iter1), decltype(iter2)>);
+  }
+
+  {
+    // iter1 in the second range
+    std::ranges::concat_view v(NonSimpleCommon{buffer_1}, NonSimpleCommonRandomAccessSized{buffer_2});
+    auto iter1 = v.begin();
+    iter1++;
+    iter1++;
+    iter1++;
+    std::ranges::iterator_t<const decltype(v)> iter2 = iter1;
+    assert(iter1 == iter2);
+    assert(*iter1 == 4);
+    assert(*iter2 == 4);
+
+    static_assert(!std::is_same_v<decltype(iter1), decltype(iter2)>);
+
+    // We cannot create a non-const iterator from a const iterator.
+    static_assert(!std::constructible_from<decltype(iter1), decltype(iter2)>);
+  }
+
+  {
+    // underlying non-const to const not convertible
+    std::ranges::concat_view v(ConstIterIncompatibleView{buffer_1}, NonSimpleCommon{buffer_2});
+    auto iter1 = v.begin();
+    auto iter2 = std::as_const(v).begin();
+
+    static_assert(!std::is_same_v<decltype(iter1), decltype(iter2)>);
+    static_assert(!std::constructible_from<decltype(iter1), decltype(iter2)>);
+    static_assert(!std::constructible_from<decltype(iter2), decltype(iter1)>);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/decrement.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/decrement.pass.cpp
new file mode 100644
index 0000000000000..99b35d3412e81
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/decrement.pass.cpp
@@ -0,0 +1,228 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// constexpr __iterator& operator--()
+
+// constexpr __iterator operator--(int)
+
+#include <array>
+#include <cassert>
+#include <ranges>
+
+#include "test_macros.h"
+#include "../../range_adaptor_types.h"
+
+template <class Iter>
+concept CanDecrement = requires(Iter it) { --it; } || requires(Iter it) { it--; };
+
+struct NonBidi : IntBufferView {
+  using IntBufferView::IntBufferView;
+  using iterator = forward_iterator<int*>;
+  constexpr iterator begin() const { return iterator(buffer_); }
+  constexpr sentinel_wrapper<iterator> end() const { return sentinel_wrapper<iterator>(iterator(buffer_ + size_)); }
+};
+
+constexpr bool test() {
+  std::array<int, 4> a{1, 2, 3, 4};
+  std::array<int, 4> b{5, 6, 7, 8};
+
+  // Test with a single view
+  {
+    std::ranges::concat_view view(a);
+    auto it = std::ranges::next(view.begin(), view.end());
+    assert(it == view.end());
+
+    auto& result = --it;
+    ASSERT_SAME_TYPE(decltype(result)&, decltype(--it));
+    assert(&result == &it);
+    assert(result == view.begin() + 3);
+  }
+
+  // Test with more than one view
+  {
+    std::ranges::concat_view view(a, b);
+    auto it = std::ranges::next(view.begin(), view.end());
+    assert(it == view.end());
+
+    auto& result = --it;
+    assert(&result == &it);
+
+    --it;
+    assert(*it == 7);
+    assert(it == view.begin() + 6);
+  }
+
+  // Test going forward and then backward on the same iterator
+  {
+    std::ranges::concat_view view(a, b);
+    auto it = view.begin();
+    ++it;
+    --it;
+    assert(*it == a[0]);
+    ++it;
+    ++it;
+    --it;
+    assert(*it == a[1]);
+    ++it;
+    ++it;
+    --it;
+    assert(*it == a[2]);
+    ++it;
+    ++it;
+    --it;
+    assert(*it == a[3]);
+  }
+
+  // Test post-decrement
+  {
+    std::ranges::concat_view view(a, b);
+    auto it = std::ranges::next(view.begin(), view.end());
+    assert(it == view.end()); // test the test
+    auto result = it--;
+    ASSERT_SAME_TYPE(decltype(result), decltype(it--));
+    assert(result == view.end());
+    assert(it == (result - 1));
+  }
+
+  // bidirectional
+  {
+    int buffer[2] = {1, 2};
+
+    std::ranges::concat_view v(BidiCommonView{buffer}, std::views::iota(0, 5));
+    auto it    = v.begin();
+    using Iter = decltype(it);
+
+    ++it;
+    ++it;
+
+    static_assert(std::is_same_v<decltype(--it), Iter&>);
+    auto& it_ref = --it;
+    assert(&it_ref == &it);
+
+    assert(it == ++v.begin());
+
+    static_assert(std::is_same_v<decltype(it--), Iter>);
+    auto tmp = it--;
+    assert(it == v.begin());
+    assert(tmp == ++v.begin());
+  }
+
+  // non bidirectional
+  {
+    int buffer[3] = {4, 5, 6};
+    std::ranges::concat_view v(a, NonBidi{buffer});
+    using Iter = std::ranges::iterator_t<decltype(v)>;
+    static_assert(!CanDecrement<Iter>);
+  }
+
+  // Cross-boundary decrement: from begin of a later range into the previous range's last element.
+  {
+    auto v = std::views::concat(a, b);
+
+    auto it = v.begin();
+    it += a.size();
+    --it;
+    assert(*it == a.back());
+
+    auto it2 = v.begin();
+    it2 += a.size();
+    auto old = it2--;
+    static_assert(std::is_same_v<decltype(old), decltype(it2)>);
+    assert(*old == b.front());
+    assert(*it2 == a.back());
+  }
+
+  // Cross-boundary with three ranges
+  {
+    std::array<int, 3> c{9, 10, 11};
+    auto v3 = std::views::concat(a, b, c);
+
+    auto it = v3.begin();
+    it += a.size() + b.size();
+    --it;
+    assert(*it == b.back());
+
+    // const-iterator.
+    const auto& cv3 = v3;
+    auto cit        = cv3.begin();
+    cit += a.size() + b.size();
+    --cit;
+    assert(*cit == b.back());
+  }
+
+  // Cross-boundary decrement where the previous range is empty.
+  {
+    std::array<int, 0> e{};
+    auto v = std::views::concat(a, e, b);
+
+    auto it = v.begin();
+    it += a.size();
+    --it; // this skips e
+    assert(*it == a.back());
+
+    auto it2 = v.begin();
+    it2 += a.size();
+    auto old = it2--;
+    assert(*old == b.front());
+    assert(*it2 == a.back());
+
+    // const-iterator
+    const auto& cv = v;
+    auto cit       = cv.begin();
+    cit += a.size();
+    --cit;
+    assert(*cit == a.back());
+  }
+
+  // multiple empty ranges in the middle
+  {
+    std::array<int, 0> e1{}, e2{};
+    auto v = std::views::concat(a, e1, e2, b);
+
+    auto it = v.begin();
+    it += a.size();
+    --it; // skip e2 and e1
+    assert(*it == a.back());
+  }
+
+  // Different range types
+  {
+    std::span<const int> sa{a};
+    auto sb = std::ranges::subrange{b.begin(), b.end()};
+    std::array<int, 2> c{9, 10};
+
+    auto v = std::views::concat(sa, sb, c);
+
+    auto it = v.begin();
+    std::ranges::advance(it, sa.size());
+    --it;
+    assert(*it == a.back());
+
+    auto it2 = v.begin();
+    std::ranges::advance(it2, sa.size() + sb.size());
+    --it2;
+    assert(*it2 == b.back());
+
+    // const-iterator.
+    const auto& cv = v;
+    auto cit       = cv.begin();
+    std::ranges::advance(cit, sa.size() + sb.size());
+    --cit;
+    assert(*cit == b.back());
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/deref.pass.cpp
new file mode 100644
index 0000000000000..eb61bbf840f4f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/deref.pass.cpp
@@ -0,0 +1,145 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// constexpr decltype(auto) operator*() const
+
+// REQUIRES: std-at-least-c++26
+
+#include <array>
+#include <cassert>
+#include <ranges>
+#include <string>
+#include <vector>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "../types.h"
+
+template <class Iter, class ValueType = int, class Sent = sentinel_wrapper<Iter>>
+constexpr void test() {
+  {
+    // test with one view
+    using View           = minimal_view<Iter, Sent>;
+    using ConcatView     = std::ranges::concat_view<View>;
+    using ConcatIterator = std::ranges::iterator_t<ConcatView>;
+
+    auto make_concat_view = [](auto begin, auto end) {
+      View view{Iter(begin), Sent(Iter(end))};
+      return ConcatView(std::move(view));
+    };
+
+    std::array array{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+    ConcatView view     = make_concat_view(array.data(), array.data() + array.size());
+    ConcatIterator iter = view.begin();
+    int& result         = *iter;
+    ASSERT_SAME_TYPE(int&, decltype(*iter));
+    assert(&result == array.data());
+  }
+
+  {
+    // test with more than one view
+    std::array<int, 3> array1{0, 1, 2};
+    std::array<int, 3> array2{0, 1, 2};
+    std::ranges::concat_view view(std::views::all(array1), std::views::all(array2));
+    decltype(auto) it1 = view.begin();
+    decltype(auto) it2 = view.begin() + 3;
+
+    ASSERT_SAME_TYPE(int&, decltype(*it1));
+    assert(*it1 == *it2);
+  }
+
+  {
+    // constness
+    constexpr static std::array<int, 3> array1{0, 1, 2};
+    constexpr static std::array<int, 3> array2{0, 1, 2};
+    constexpr static std::ranges::concat_view view(std::views::all(array1), std::views::all(array2));
+    decltype(auto) it1 = view.begin();
+    decltype(auto) it2 = view.begin() + 3;
+
+    ASSERT_SAME_TYPE(const int&, decltype(*it1));
+    assert(*it1 == *it2);
+  }
+}
+
+constexpr bool tests() {
+  test<cpp17_input_iterator<int*>>();
+  test<forward_iterator<int*>>();
+  test<bidirectional_iterator<int*>>();
+  test<random_access_iterator<int*>>();
+  test<contiguous_iterator<int*>>();
+  test<int*>();
+
+  {
+    // test with more than one view of 
diff erent types
+    std::vector<int> array1{0, 1, 2};
+    std::array<int, 3> array2{3, 4, 5};
+    std::ranges::concat_view cv(std::views::all(array1), std::views::all(array2));
+    decltype(auto) it1 = cv.begin();
+    decltype(auto) it2 = cv.begin();
+
+    ASSERT_SAME_TYPE(int&, decltype(*it1));
+    it2++;
+    assert(*it1 == 0);
+    assert(*it2 == 1);
+    it2++;
+    it2++;
+    assert(*it2 == 3);
+  }
+
+  {
+    // test concat-reference-t
+    {
+      //  const std::string&  +  std::string_view  +  const std::string&
+      const std::array<std::string, 2> left  = {"L0", "L1"};
+      std::array<std::string_view, 1> mid    = {std::string_view{"M0"}};
+      const std::array<std::string, 1> right = {"R0"};
+
+      auto v   = std::views::concat(left, mid, right);
+      auto it  = v.begin();
+      auto cit = std::as_const(v).begin();
+
+      //  Common reference of {const std::string&, std::string_view&, const std::string&}  ==>  std::string_view
+      static_assert(std::is_same_v<decltype(*it), std::string_view>);
+      static_assert(std::is_same_v<decltype(*cit), std::string_view>);
+
+      assert(*it == "L0");
+      assert(*std::next(it, 1) == "L1");
+      assert(*std::next(it, 2) == "M0");
+      assert(*std::next(it, 3) == "R0");
+    }
+
+    {
+      // std::string&  +  std::string (prvalue)  +  const std::string&
+      std::array<std::string, 1> left        = {"L"};
+      std::vector<std::string> mid           = {"M"};
+      const std::array<std::string, 1> right = {"R"};
+
+      auto mid_prvalue = mid | std::views::transform([](const std::string& s) { return s; });
+
+      auto v   = std::views::concat(left, mid_prvalue, right);
+      auto it  = v.begin();
+      auto cit = std::as_const(v).begin();
+
+      // Common reference of {std::string&, std::string (prvalue), const std::string&}  ==>  const std::string
+      static_assert(std::is_same_v<decltype(*it), const std::string>);
+      static_assert(std::is_same_v<decltype(*cit), const std::string>);
+
+      assert(*it == "L");
+      assert(*std::next(it, 1) == "M");
+      assert(*std::next(it, 2) == "R");
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  tests();
+  static_assert(tests());
+  return 0;
+}

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/increment.pass.cpp
new file mode 100644
index 0000000000000..071d71635950e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/increment.pass.cpp
@@ -0,0 +1,206 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// constexpr __iterator& operator++()
+
+// constexpr void operator++(int)
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <ranges>
+#include <stddef.h>
+#include <type_traits>
+#include <utility>
+
+#include "test_iterators.h"
+#include "../../range_adaptor_types.h"
+
+struct InputRange {
+  using iterator = cpp17_input_iterator<int*>;
+  int* begin_;
+  int* end_;
+  constexpr InputRange(int* b, int* e) : begin_(b), end_(e) {}
+  constexpr iterator begin() const { return iterator(begin_); }
+  constexpr sentinel_wrapper<iterator> end() const { return sentinel_wrapper<iterator>(iterator(end_)); }
+};
+
+static_assert(!std::ranges::forward_range<InputRange>);
+static_assert(std::ranges::input_range<InputRange>);
+
+constexpr bool test() {
+  std::array<int, 4> a{1, 2, 3, 4};
+  std::array<double, 4> b{1.0, 2.0, 3.0};
+
+  // one view
+  {
+    std::ranges::concat_view view(a);
+    auto it    = view.begin();
+    using Iter = decltype(it);
+    static_assert(std::is_same_v<decltype(it++), Iter>);
+    static_assert(std::is_same_v<decltype(++it), Iter&>);
+
+    auto& result = ++it;
+    assert(&result == &it);
+    assert(*result == 2);
+
+    it = view.begin();
+    assert(*it++ == 1);
+    assert(*it == 2);
+  }
+
+  // more than one view
+  {
+    std::ranges::concat_view view(a, b);
+    auto it    = view.begin();
+    using Iter = decltype(it);
+    static_assert(std::is_same_v<decltype(it++), Iter>);
+    static_assert(std::is_same_v<decltype(++it), Iter&>);
+    auto& result = ++it;
+    assert(&result == &it);
+    assert(*result == 2);
+
+    it       = view.begin();
+    auto old = it++;
+    assert(*old == 1);
+    assert(*it == 2);
+  }
+
+  // more than two views
+  {
+    std::ranges::concat_view view(a, b, std::views::iota(0, 5));
+    auto it    = view.begin();
+    using Iter = decltype(it);
+    static_assert(std::is_same_v<decltype(it++), Iter>);
+    static_assert(std::is_same_v<decltype(++it), Iter&>);
+    auto& result = ++it;
+    assert(&result == &it);
+    assert(*result == 2);
+  }
+
+  // input range, no postfix operator++
+  {
+    int buffer[3] = {4, 5, 6};
+    std::ranges::concat_view view(a, InputRange{buffer, buffer + 3});
+    auto it    = view.begin();
+    using Iter = decltype(it);
+    static_assert(std::is_same_v<decltype(it++), void>);
+    static_assert(std::is_same_v<decltype(++it), Iter&>);
+    auto& result = ++it;
+    assert(&result == &it);
+    assert(*result == 2);
+  }
+
+  // Increment an iterator multiple times
+  {
+    std::ranges::concat_view view(a);
+
+    auto it = view.begin();
+    assert(*it == a[0]);
+
+    ++it;
+    assert(*it == a[1]);
+    ++it;
+    assert(*it == a[2]);
+    ++it;
+    assert(*it == a[3]);
+
+    it       = view.begin();
+    auto old = it++;
+    assert(*old == a[0]);
+    assert(*it == a[1]);
+  }
+
+  // Different underlying range types; ++ crosses from end of first into start of second.
+  {
+    std::span<const int> sa{a};
+    auto sb = std::ranges::subrange{b.data(), b.data() + b.size()};
+    auto v  = std::views::concat(sa, sb);
+
+    auto it = v.begin();
+    for (size_t i = 1; i < a.size(); i++) {
+      ++it;
+    }
+    assert(*it == a.back());
+
+    ++it;
+    assert(*it == b.front());
+
+    auto it2 = v.begin();
+    for (size_t i = 1; i < a.size(); i++) {
+      ++it2;
+    }
+    auto old = it2++;
+    assert(*old == a.back());
+    assert(*it2 == b.front());
+
+    // Same with a const-iterator.
+    const auto& cv = v;
+    auto cit       = cv.begin();
+    for (size_t i = 1; i < a.size(); i++) {
+      ++cit;
+    }
+    ++cit;
+    assert(*cit == b.front());
+  }
+
+  // ++ crosses into next range when that next range is empty.
+  {
+    std::array<int, 0> e{};
+    auto v = std::views::concat(a, e, b);
+
+    auto it = v.begin();
+    for (size_t i = 1; i < a.size(); i++) {
+      ++it;
+    }
+    ++it; //  skip e
+    assert(*it == b.front());
+
+    auto it2 = v.begin();
+    for (size_t i = 1; i < a.size(); i++)
+      ++it2;
+    auto old = it2++;
+    assert(*old == a.back());
+    assert(*it2 == b.front());
+
+    // Const-iterator.
+    const auto& cv = v;
+    auto cit       = cv.begin();
+    for (size_t i = 1; i < a.size(); i++)
+      ++cit;
+    ++cit;
+    assert(*cit == b.front());
+  }
+
+  // Multiple consecutive empty ranges are skipped on ++.
+  {
+    std::array<int, 0> e1{}, e2{};
+    auto v = std::views::concat(a, e1, e2, b);
+
+    auto it = v.begin();
+    for (size_t i = 1; i < a.size(); i++) {
+      ++it;
+    }
+
+    ++it; // skip e1 and e2
+    assert(*it == b.front());
+    auto old = it++;
+    assert(*old == b.front());
+    assert(*it == b[1]);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/iter_move.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/iter_move.pass.cpp
new file mode 100644
index 0000000000000..cad6687f8b5fa
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/iter_move.pass.cpp
@@ -0,0 +1,235 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// friend constexpr decltype(auto) iter_move(const __iterator& __it) noexcept(
+//       ((is_nothrow_invocable_v< decltype(ranges::iter_move), const iterator_t<__maybe_const<_Const, _Views>>& > &&
+//         is_nothrow_convertible_v< range_rvalue_reference_t<__maybe_const<_Const, _Views>>,
+//                                   __concat_rvalue_reference_t<__maybe_const<_Const, _Views>...> >) &&
+//        ...))
+
+// REQUIRES: std-at-least-c++26
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <ranges>
+#include <utility>
+
+#include "../types.h"
+
+struct ConvMayThrow {
+  int v;
+  operator int() { return v; }
+};
+
+struct ConvNoThrow {
+  int v;
+  operator int() noexcept { return v; }
+};
+
+template <typename T, bool NoThrow>
+struct ThowingIter {
+  using iterator_concept  = std::forward_iterator_tag;
+  using iterator_category = std::forward_iterator_tag;
+  using 
diff erence_type   = std::ptr
diff _t;
+  using value_type        = T;
+
+  T* p = nullptr;
+
+  constexpr T& operator*() const noexcept { return *p; }
+  constexpr ThowingIter& operator++() noexcept {
+    ++p;
+    return *this;
+  }
+  constexpr ThowingIter operator++(int) noexcept {
+    ThowingIter tmp = *this;
+    ++*this;
+    return tmp;
+  }
+  friend constexpr bool operator==(ThowingIter, ThowingIter) = default;
+
+  friend constexpr decltype(auto) iter_move(const ThowingIter& it) noexcept(NoThrow) { return std::move(*it.p); }
+};
+
+struct Range : std::ranges::view_base {
+  using Iterator = forward_iterator<int*>;
+  using Sentinel = sentinel_wrapper<Iterator>;
+  constexpr explicit Range(int* b, int* e) : begin_(b), end_(e) {}
+  constexpr Iterator begin() const { return Iterator(begin_); }
+  constexpr Sentinel end() const { return Sentinel(Iterator(end_)); }
+
+private:
+  int* begin_;
+  int* end_;
+};
+
+template <class Iter, class Sentinel>
+struct MiniView : std::ranges::view_base {
+  Iter b{};
+  Sentinel e{};
+  constexpr MiniView() = default;
+  constexpr MiniView(Iter first, Sentinel last) : b(first), e(last) {}
+  constexpr Iter begin() const noexcept { return b; }
+  constexpr Sentinel end() const noexcept { return e; }
+};
+
+constexpr bool test() {
+  int arr[2]                   = {1, 2};
+  ConvMayThrow maythrow_buf[2] = {{3}, {4}};
+  ConvNoThrow nothrow_buf[2]   = {{5}, {6}};
+  {
+    // All underlying iter_move are noexcept
+    // => concat iter_move has noexcept(true)
+    using Iter_NoThrow     = ThowingIter<int, true>;
+    using Sentinel_NoThrow = sentinel_wrapper<Iter_NoThrow>;
+    using View_NoThrow     = MiniView<Iter_NoThrow, Sentinel_NoThrow>;
+    View_NoThrow v1(Iter_NoThrow(arr), Sentinel_NoThrow(Iter_NoThrow(arr + 1)));
+    View_NoThrow v2(Iter_NoThrow(arr), Sentinel_NoThrow(Iter_NoThrow(arr + 1)));
+
+    auto cv     = std::views::concat(v1, v2);
+    using Iter  = decltype(cv.begin());
+    using CIter = decltype(std::as_const(cv).begin());
+
+    static_assert(noexcept(std::ranges::iter_move(std::declval<Iter>())));
+    static_assert(noexcept(std::ranges::iter_move(std::declval<CIter>())));
+
+    auto it                              = cv.begin();
+    std::same_as<int&&> decltype(auto) x = std::ranges::iter_move(it);
+    assert(x == 1);
+  }
+
+  {
+    // All underlying iter_move are noexcept
+    // underlying ranges have 
diff erent
+    // => concat iter_move has noexcept(true)
+    using Iter_NoThrow     = ThowingIter<int, true>;
+    using Sentinel_NoThrow = sentinel_wrapper<Iter_NoThrow>;
+    using View_NoThrow     = MiniView<Iter_NoThrow, Sentinel_NoThrow>;
+    View_NoThrow v1(Iter_NoThrow(arr), Sentinel_NoThrow(Iter_NoThrow(arr + 1)));
+    View_NoThrow v2(Iter_NoThrow(arr), Sentinel_NoThrow(Iter_NoThrow(arr + 1)));
+
+    auto cv     = std::views::concat(v1, v2);
+    using Iter  = decltype(cv.begin());
+    using CIter = decltype(std::as_const(cv).begin());
+
+    static_assert(noexcept(std::ranges::iter_move(std::declval<Iter>())));
+    static_assert(noexcept(std::ranges::iter_move(std::declval<CIter>())));
+
+    auto it                              = cv.begin();
+    std::same_as<int&&> decltype(auto) x = std::ranges::iter_move(it);
+    assert(x == 1);
+  }
+
+  {
+    // One underlying may throw
+    // concat iter_move has noexcept(false)
+    using Iter_NoThrow     = ThowingIter<int, true>;
+    using Iter_Throw       = ThowingIter<int, false>;
+    using Sentinel_NoThrow = sentinel_wrapper<Iter_NoThrow>;
+    using Sentinel_Throw   = sentinel_wrapper<Iter_Throw>;
+    using View_NoThrow     = MiniView<Iter_NoThrow, Sentinel_NoThrow>;
+    using View_Throw       = MiniView<Iter_Throw, Sentinel_Throw>;
+
+    auto cv = std::views::concat(View_NoThrow{Iter_NoThrow{arr}, Sentinel_NoThrow{Iter_NoThrow{arr + 1}}},
+                                 View_Throw{Iter_Throw{arr}, Sentinel_Throw{Iter_Throw{arr + 1}}});
+
+    using Iter  = decltype(cv.begin());
+    using CIter = decltype(std::as_const(cv).begin());
+
+    static_assert(!noexcept(std::ranges::iter_move(std::declval<Iter>())));
+    static_assert(!noexcept(std::ranges::iter_move(std::declval<CIter>())));
+
+    auto it                              = cv.begin();
+    std::same_as<int&&> decltype(auto) x = std::ranges::iter_move(it);
+    assert(x == 1);
+  }
+
+  {
+    // one underlying iter_move may throw, convert ConvNoThrow to int has noexcept
+    // => iter_move has noexcept(false)
+    using Iter_NoThrow         = ThowingIter<int, true>;
+    using IterConv_NoThrow     = ThowingIter<ConvNoThrow, false>;
+    using Sentinel_NoThrow     = sentinel_wrapper<Iter_NoThrow>;
+    using SentinelConv_NoThrow = sentinel_wrapper<IterConv_NoThrow>;
+    using View_NoThrow         = MiniView<Iter_NoThrow, Sentinel_NoThrow>;
+    using ViewHasConv_NoThrow  = MiniView<IterConv_NoThrow, SentinelConv_NoThrow>;
+
+    auto cv = std::views::concat(
+        View_NoThrow{Iter_NoThrow{arr}, Sentinel_NoThrow{Iter_NoThrow{arr + 1}}},
+        ViewHasConv_NoThrow{IterConv_NoThrow{nothrow_buf}, SentinelConv_NoThrow{IterConv_NoThrow{nothrow_buf + 1}}});
+
+    using Iter  = decltype(cv.begin());
+    using CIter = decltype(std::as_const(cv).begin());
+
+    static_assert(!noexcept(std::ranges::iter_move(std::declval<Iter>())));
+    static_assert(!noexcept(std::ranges::iter_move(std::declval<CIter>())));
+
+    auto it                            = cv.begin();
+    std::same_as<int> decltype(auto) x = std::ranges::iter_move(it);
+    assert(x == 1);
+  }
+
+  {
+    // all underlying iter_move has noexcept, and convert ConvNoThrow to int has noexcept
+    // => concat iter_move has noexcept(true)
+    using Iter_NoThrow         = ThowingIter<int, true>;
+    using IterConv_NoThrow     = ThowingIter<ConvNoThrow, true>;
+    using Sentinel_NoThrow     = sentinel_wrapper<Iter_NoThrow>;
+    using SentinelConv_NoThrow = sentinel_wrapper<IterConv_NoThrow>;
+    using View_NoThrow         = MiniView<Iter_NoThrow, Sentinel_NoThrow>;
+    using ViewHasConv_NoThrow  = MiniView<IterConv_NoThrow, SentinelConv_NoThrow>;
+
+    auto cv = std::views::concat(
+        View_NoThrow{Iter_NoThrow{arr}, Sentinel_NoThrow{Iter_NoThrow{arr + 1}}},
+        ViewHasConv_NoThrow{IterConv_NoThrow{nothrow_buf}, SentinelConv_NoThrow{IterConv_NoThrow{nothrow_buf + 1}}});
+
+    using Iter  = decltype(cv.begin());
+    using CIter = decltype(std::as_const(cv).begin());
+
+    static_assert(noexcept(std::ranges::iter_move(std::declval<Iter>())));
+    static_assert(noexcept(std::ranges::iter_move(std::declval<CIter>())));
+
+    auto it                            = cv.begin();
+    std::same_as<int> decltype(auto) x = std::ranges::iter_move(it);
+    assert(x == 1);
+  }
+
+  {
+    // underlying iter_move has noexcept, but convert ConvMayThrow to int is noexcept(false)
+    // => concat iter_move has noexcept(false)
+    using Iter_NoThrow          = ThowingIter<int, true>;
+    using IterConv_MayThrow     = ThowingIter<ConvMayThrow, true>;
+    using Sentinel_NoThrow      = sentinel_wrapper<Iter_NoThrow>;
+    using SentinelConv_MayThrow = sentinel_wrapper<IterConv_MayThrow>;
+    using View_NoThrow          = MiniView<Iter_NoThrow, Sentinel_NoThrow>;
+    using ViewHasConv_MayThrow  = MiniView<IterConv_MayThrow, SentinelConv_MayThrow>;
+
+    auto cv = std::views::concat(
+        View_NoThrow{Iter_NoThrow{arr}, Sentinel_NoThrow{Iter_NoThrow{arr + 1}}},
+        ViewHasConv_MayThrow{
+            IterConv_MayThrow{maythrow_buf}, SentinelConv_MayThrow{IterConv_MayThrow{maythrow_buf + 1}}});
+
+    using Iter  = decltype(cv.begin());
+    using CIter = decltype(std::as_const(cv).begin());
+
+    static_assert(!noexcept(std::ranges::iter_move(std::declval<Iter>())));
+    static_assert(!noexcept(std::ranges::iter_move(std::declval<CIter>())));
+
+    auto it                            = cv.begin();
+    std::same_as<int> decltype(auto) x = std::ranges::iter_move(it);
+    assert(x == 1);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/iter_swap.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/iter_swap.pass.cpp
new file mode 100644
index 0000000000000..a647de4fe913f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/iter_swap.pass.cpp
@@ -0,0 +1,199 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// friend constexpr void iter_swap(const __iterator& __x, const __iterator& __y)
+//     noexcept((noexcept(ranges::swap(*__x, *__y))) &&
+//               (noexcept(ranges::iter_swap(std::declval<const iterator_t<__maybe_const<_Const, _Views>>>(),
+//                                           std::declval<const iterator_t<__maybe_const<_Const, _Views>>>())) &&
+//               ...))
+
+// REQUIRES: std-at-least-c++26
+
+#include <array>
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <utility>
+
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "../types.h"
+#include "../../range_adaptor_types.h"
+
+template <class It>
+concept has_iter_swap = requires(It it) { std::ranges::iter_swap(it, it); };
+
+struct Elem {
+  int v;
+  friend constexpr void swap(Elem& a, Elem& b) noexcept { std::ranges::swap(a.v, b.v); }
+};
+
+template <typename T>
+struct SwapIter {
+  using iterator_concept  = std::forward_iterator_tag;
+  using iterator_category = std::forward_iterator_tag;
+  using 
diff erence_type   = std::ptr
diff _t;
+  using value_type        = T;
+
+  T* p = nullptr;
+
+  constexpr T& operator*() const noexcept { return *p; }
+  constexpr SwapIter& operator++() noexcept {
+    ++p;
+    return *this;
+  }
+  constexpr SwapIter operator++(int) noexcept {
+    SwapIter tmp = *this;
+    ++*this;
+    return tmp;
+  }
+  friend constexpr bool operator==(SwapIter, SwapIter) = default;
+
+  friend constexpr void iter_swap(const SwapIter& it1, const SwapIter& it2) { std::ranges::swap(*it1, *it2); }
+};
+
+template <typename T>
+struct SwapIterNoCustom {
+  using iterator_concept  = std::forward_iterator_tag;
+  using iterator_category = std::forward_iterator_tag;
+  using 
diff erence_type   = std::ptr
diff _t;
+  using value_type        = T;
+
+  T* p = nullptr;
+
+  constexpr T& operator*() const noexcept { return *p; }
+  constexpr SwapIterNoCustom& operator++() noexcept {
+    ++p;
+    return *this;
+  }
+  constexpr SwapIterNoCustom operator++(int) noexcept {
+    SwapIterNoCustom tmp = *this;
+    ++*this;
+    return tmp;
+  }
+  friend constexpr bool operator==(SwapIterNoCustom, SwapIterNoCustom) = default;
+};
+
+template <typename T>
+struct SwapIterTracked {
+  using iterator_concept  = std::forward_iterator_tag;
+  using iterator_category = std::forward_iterator_tag;
+  using 
diff erence_type   = std::ptr
diff _t;
+  using value_type        = T;
+
+  T* p                        = nullptr;
+  int* iter_swap_called_times = nullptr;
+
+  constexpr T& operator*() const noexcept { return *p; }
+  constexpr SwapIterTracked& operator++() noexcept {
+    ++p;
+    return *this;
+  }
+  constexpr SwapIterTracked operator++(int) noexcept {
+    SwapIterTracked tmp = *this;
+    ++*this;
+    return tmp;
+  }
+  friend constexpr bool operator==(const SwapIterTracked& x, const SwapIterTracked& y) { return x.p == y.p; }
+
+  friend constexpr void iter_swap(const SwapIterTracked& x, const SwapIterTracked& y) {
+    if (x.iter_swap_called_times)
+      ++*x.iter_swap_called_times;
+    std::ranges::swap(*x, *y);
+  }
+};
+
+template <typename Iter, class Sentinel>
+struct MiniView : std::ranges::view_base {
+  Iter b{};
+  Sentinel e{};
+  constexpr MiniView() = default;
+  constexpr MiniView(Iter first, Sentinel last) : b(first), e(last) {}
+  constexpr Iter begin() const noexcept { return b; }
+  constexpr Sentinel end() const noexcept { return e; }
+};
+
+constexpr bool test() {
+  using IteratorA = SwapIter<Elem>;
+  using SentinelA = sentinel_wrapper<IteratorA>;
+  using IteratorB = SwapIterNoCustom<Elem>;
+  using SentinelB = sentinel_wrapper<IteratorB>;
+  using ViewA     = MiniView<IteratorA, SentinelA>;
+  using ViewB     = MiniView<IteratorB, SentinelB>;
+
+  {
+    Elem a1[2]{{1}, {2}};
+    Elem a2[2]{{3}, {4}};
+
+    ViewA v1{IteratorA(a1), SentinelA(IteratorA(a1 + 2))};
+    ViewB v2{IteratorB(a2), SentinelB(IteratorB(a2 + 2))};
+
+    std::ranges::concat_view cv(v1, v2);
+
+    auto it1 = cv.begin();
+    auto it2 = ++cv.begin();
+    it2++;
+
+    // always false: https://cplusplus.github.io/LWG/lwg-active.html#4489
+    static_assert(noexcept(std::ranges::iter_swap(it1, it2)) == false);
+
+    std::ranges::iter_swap(it1, it2);
+
+    // iter_swap
+    assert(a1[0].v == 3 && a2[0].v == 1);
+  }
+
+  // Test that the underlying iterator's iter_swap specialization is called
+  // when both iterators point to the same underlying range
+  {
+    int iter_swap_call_count = 0;
+    Elem a1[3]{{10}, {20}, {30}};
+    Elem a2[2]{{40}, {50}};
+
+    using TrackedIter     = SwapIterTracked<Elem>;
+    using TrackedSentinel = sentinel_wrapper<TrackedIter>;
+    using TrackedView     = MiniView<TrackedIter, TrackedSentinel>;
+
+    TrackedView tv{TrackedIter{a1, &iter_swap_call_count}, TrackedSentinel{TrackedIter{a1 + 3, &iter_swap_call_count}}};
+    ViewB vb{IteratorB{a2}, SentinelB{IteratorB{a2 + 2}}};
+
+    std::ranges::concat_view cv(tv, vb);
+
+    auto it1 = cv.begin();
+    auto it2 = ++cv.begin();
+
+    assert(iter_swap_call_count == 0);
+    std::ranges::iter_swap(it1, it2);
+    // The underlying iter_swap specialization should have been called
+    assert(iter_swap_call_count == 1);
+    assert(a1[0].v == 20 && a1[1].v == 10);
+
+    // Call again to verify the count keeps incrementing
+    std::ranges::iter_swap(it1, it2);
+    assert(iter_swap_call_count == 2);
+    assert(a1[0].v == 10 && a1[1].v == 20);
+  }
+
+  // Test that iter_swap requires the underlying iterator to be iter_swappable
+  {
+    using Iterator       = int const*;
+    using View           = minimal_view<Iterator, Iterator>;
+    using ConcatView     = std::ranges::concat_view<View>;
+    using ConcatIterator = std::ranges::iterator_t<ConcatView>;
+    static_assert(!std::indirectly_swappable<Iterator>);
+    static_assert(!has_iter_swap<ConcatIterator>);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/member_types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/member_types.compile.pass.cpp
new file mode 100644
index 0000000000000..a105641717562
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/member_types.compile.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// test iterator_concept, iterator_category, 
diff erence_type, value_type
+
+#include <array>
+#include <forward_list>
+#include <list>
+#include <ranges>
+#include <tuple>
+#include <utility>
+
+#include "test_iterators.h"
+#include "../../range_adaptor_types.h"
+
+template <class T>
+struct ForwardView : std::ranges::view_base {
+  forward_iterator<T*> begin() const;
+  sentinel_wrapper<forward_iterator<T*>> end() const;
+};
+
+template <class T>
+struct InputView : std::ranges::view_base {
+  cpp17_input_iterator<T*> begin() const;
+  sentinel_wrapper<cpp17_input_iterator<T*>> end() const;
+};
+
+template <class T>
+concept HasIterCategory = requires { typename T::iterator_category; };
+
+template <class T>
+struct DiffTypeIter {
+  using iterator_category = std::input_iterator_tag;
+  using value_type        = int;
+  using 
diff erence_type   = T;
+
+  int operator*() const;
+  DiffTypeIter& operator++();
+  void operator++(int);
+  friend constexpr bool operator==(DiffTypeIter, DiffTypeIter) = default;
+};
+
+template <class T>
+struct DiffTypeRange {
+  DiffTypeIter<T> begin() const;
+  DiffTypeIter<T> end() const;
+};
+
+struct Foo {};
+struct Bar {};
+
+struct ConstVeryDifferentRange {
+  int* begin();
+  int* end();
+
+  forward_iterator<double*> begin() const;
+  forward_iterator<double*> end() const;
+};
+
+constexpr bool test() {
+  int buffer[] = {1, 2, 3, 4};
+  {
+    // random_access_iterator_tag
+    std::ranges::concat_view v(buffer, buffer);
+    using Iter = decltype(v.begin());
+
+    static_assert(std::is_same_v<Iter::iterator_concept, std::random_access_iterator_tag>);
+    static_assert(std::is_same_v<Iter::iterator_category, std::random_access_iterator_tag>);
+    static_assert(std::is_same_v<Iter::
diff erence_type, std::ptr
diff _t>);
+    static_assert(std::is_same_v<Iter::value_type, int>);
+    static_assert(HasIterCategory<Iter>);
+  }
+
+  {
+    // 3 views
+    std::ranges::concat_view v(buffer, buffer, buffer);
+    using Iter = decltype(v.begin());
+
+    static_assert(std::is_same_v<Iter::iterator_concept, std::random_access_iterator_tag>);
+    static_assert(std::is_same_v<Iter::iterator_category, std::random_access_iterator_tag>);
+    static_assert(std::is_same_v<Iter::
diff erence_type, std::ptr
diff _t>);
+    static_assert(std::is_same_v<Iter::value_type, int>);
+    static_assert(HasIterCategory<Iter>);
+  }
+
+  {
+    // bidirectional_iterator_tag
+    std::ranges::concat_view v(BidiCommonView{buffer});
+    using Iter = decltype(v.begin());
+
+    static_assert(std::is_same_v<Iter::iterator_concept, std::bidirectional_iterator_tag>);
+    static_assert(std::is_same_v<Iter::iterator_category, std::bidirectional_iterator_tag>);
+    static_assert(std::is_same_v<Iter::
diff erence_type, std::ptr
diff _t>);
+    static_assert(std::is_same_v<Iter::value_type, int>);
+  }
+
+  {
+    // forward_iterator_tag
+    using Iter = std::ranges::iterator_t<std::ranges::concat_view<ForwardView<int>>>;
+
+    static_assert(std::is_same_v<Iter::iterator_concept, std::forward_iterator_tag>);
+    static_assert(std::is_same_v<Iter::iterator_category, std::forward_iterator_tag>);
+    static_assert(std::is_same_v<Iter::
diff erence_type, std::ptr
diff _t>);
+    static_assert(std::is_same_v<Iter::value_type, int>);
+    static_assert(HasIterCategory<Iter>);
+  }
+
+  {
+    // input_iterator_tag
+    using Iter = std::ranges::iterator_t<std::ranges::concat_view<InputView<int>>>;
+
+    static_assert(std::is_same_v<Iter::iterator_concept, std::input_iterator_tag>);
+    static_assert(!HasIterCategory<Iter>);
+    static_assert(std::is_same_v<Iter::
diff erence_type, std::ptr
diff _t>);
+    static_assert(std::is_same_v<Iter::value_type, int>);
+  }
+
+  {
+    // 
diff erence_type of single view
+    std::ranges::concat_view v{DiffTypeRange<std::intptr_t>{}};
+    using Iter = decltype(v.begin());
+    static_assert(std::is_same_v<Iter::
diff erence_type, std::intptr_t>);
+  }
+
+  {
+    // value_type of single view
+    std::ranges::concat_view v{DiffTypeRange<std::intptr_t>{}};
+    using Iter = decltype(v.begin());
+    static_assert(std::is_same_v<Iter::value_type, int>);
+  }
+
+  {
+    // 
diff erence_type of multiple views should be the common type
+    std::ranges::concat_view v{DiffTypeRange<std::intptr_t>{}, DiffTypeRange<std::ptr
diff _t>{}};
+    using Iter = decltype(v.begin());
+    static_assert(std::is_same_v<Iter::
diff erence_type,
+                                 std::common_type_t< std::ranges::range_
diff erence_t<DiffTypeRange<std::intptr_t>>,
+                                                     std::ranges::range_
diff erence_t<DiffTypeRange<std::ptr
diff _t>>>>);
+  }
+
+  {
+    // value_type of multiple views should be the common type
+    std::ranges::concat_view v{DiffTypeRange<std::intptr_t>{}, DiffTypeRange<std::ptr
diff _t>{}};
+    using Iter = decltype(v.begin());
+    static_assert(std::is_same_v<Iter::value_type,
+                                 std::common_type_t< std::ranges::range_value_t<DiffTypeRange<std::intptr_t>>,
+                                                     std::ranges::range_value_t<DiffTypeRange<std::ptr
diff _t>>>>);
+  }
+
+  const std::array foos{Foo{}};
+  {
+    // value_type of user-defined type
+    std::ranges::concat_view v{foos};
+    using Iter = decltype(v.begin());
+    static_assert(std::is_same_v<Iter::value_type, Foo>);
+  }
+
+  {
+    // const-iterator 
diff erent from iterator
+    std::ranges::concat_view v{ConstVeryDifferentRange{}};
+    using Iter      = decltype(v.begin());
+    using ConstIter = decltype(std::as_const(v).begin());
+
+    static_assert(std::is_same_v<Iter::iterator_concept, std::random_access_iterator_tag>);
+    static_assert(std::is_same_v<Iter::iterator_category, std::random_access_iterator_tag>);
+    static_assert(std::is_same_v<Iter::
diff erence_type, std::ptr
diff _t>);
+    static_assert(std::is_same_v<Iter::value_type, int>);
+
+    static_assert(std::is_same_v<ConstIter::iterator_concept, std::forward_iterator_tag>);
+    static_assert(std::is_same_v<ConstIter::iterator_category, std::forward_iterator_tag>);
+    static_assert(std::is_same_v<ConstIter::
diff erence_type, std::ptr
diff _t>);
+    static_assert(std::is_same_v<ConstIter::value_type, double>);
+  }
+
+  {
+    // If is_reference_v<concat-reference-t<maybe-const<Const, Views>...>> is false,
+    // then iterator_category denotes input_iterator_tag.
+    auto v1  = std::views::iota(0, 3);
+    auto v2  = std::views::iota(4, 6);
+    auto cat = std::views::concat(v1, v2);
+
+    using Iter      = decltype(cat.begin());
+    using ConstIter = decltype(std::as_const(cat).begin());
+
+    static_assert(std::is_same_v<typename Iter::iterator_category, std::input_iterator_tag>);
+    static_assert(std::is_same_v<typename ConstIter::iterator_category, std::input_iterator_tag>);
+  }
+
+  {
+    // iterator category test
+    {
+      // all random-access => iterator_category is random_access_iterator_tag
+      std::span<int> s{buffer};                                // span<int> (RA, common)
+      auto sr = std::ranges::subrange{buffer, buffer + 4};     // subrange<pointer,pointer> (RA, common)
+      std::array<int, 2> arr{9, 10};                           // array (RA, common)
+      std::ranges::concat_view v(s, sr, std::views::all(arr)); // 
diff erent types, all RA + non-last common
+
+      using Iter  = decltype(v.begin());
+      using CIter = decltype(std::as_const(v).begin());
+      static_assert(std::is_same_v<typename Iter::iterator_category, std::random_access_iterator_tag>);
+      static_assert(std::is_same_v<typename CIter::iterator_category, std::random_access_iterator_tag>);
+    }
+
+    // random-access + bidirectional => iterator_category is bidirectional_iterator_tag
+    {
+      std::span<int> s{buffer};    // RA
+      std::list<int> lst{1, 2, 3}; // bidirectional
+      std::ranges::concat_view v(s, lst);
+
+      using Iter  = decltype(v.begin());
+      using CIter = decltype(std::as_const(v).begin());
+      static_assert(std::is_same_v<typename Iter::iterator_category, std::bidirectional_iterator_tag>);
+      static_assert(std::is_same_v<typename CIter::iterator_category, std::bidirectional_iterator_tag>);
+    }
+
+    // random-access + forward => iterator_category is forward_iterator_tag
+    {
+      std::span<int> s{buffer};           // RA
+      std::forward_list<int> fl{1, 2, 3}; // forward
+      std::ranges::concat_view v(s, fl);
+
+      using Iter  = decltype(v.begin());
+      using CIter = decltype(std::as_const(v).begin());
+      static_assert(std::is_same_v<typename Iter::iterator_category, std::forward_iterator_tag>);
+      static_assert(std::is_same_v<typename CIter::iterator_category, std::forward_iterator_tag>);
+    }
+
+    // RA + forward + RA => iterator_category is forward_iterator_tag
+    {
+      std::span<int> s1{buffer};       // RA
+      std::forward_list<int> fl{1, 2}; // forward
+      std::array<int, 1> tail{42};     // RA
+      std::ranges::concat_view v(s1, fl, tail);
+
+      using Iter  = decltype(v.begin());
+      using CIter = decltype(std::as_const(v).begin());
+      static_assert(std::is_same_v<typename Iter::iterator_category, std::forward_iterator_tag>);
+      static_assert(std::is_same_v<typename CIter::iterator_category, std::forward_iterator_tag>);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/subscript.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/subscript.pass.cpp
new file mode 100644
index 0000000000000..a509555711402
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/subscript.pass.cpp
@@ -0,0 +1,82 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// constexpr auto operator[](
diff erence_type n) const requires
+//        all_random_access<Const, Views...>
+
+#include <ranges>
+#include <cassert>
+
+#include "../../range_adaptor_types.h"
+
+constexpr bool test() {
+  int buffer1[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+  int buffer2[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  {
+    // random_access_range and common range
+    std::ranges::concat_view v(SimpleCommonRandomAccessSized{buffer1});
+    auto it = v.begin();
+    assert(it[0] == *it);
+    assert(it[2] == *(it + 2));
+    assert(it[4] == *(it + 4));
+
+    static_assert(std::is_same_v<decltype(it[2]), const int&>);
+  }
+
+  {
+    // random_access_range and common range, with last view is not a common range
+    std::ranges::concat_view v(SimpleCommonRandomAccessSized{buffer1}, SimpleNonCommonRandomAccessSized{buffer2});
+    auto it = v.begin();
+    assert(it[0] == *it);
+    assert(it[2] == *(it + 2));
+    assert(it[4] == *(it + 4));
+
+    static_assert(std::is_same_v<decltype(it[2]), const int&>);
+  }
+
+  {
+    // random_access_range and non common range
+    std::ranges::concat_view v(SimpleNonCommonRandomAccessSized{buffer1}, NonSimpleCommonRandomAccessSized{buffer2});
+    auto it                 = v.begin();
+    const auto canSubscript = [](auto&& jt) { return requires { jt[0]; }; };
+    static_assert(!canSubscript(it));
+
+    static_assert(std::is_same_v<decltype(*it), const int&>);
+  }
+
+  {
+    // contiguous_range
+    std::ranges::concat_view v(ContiguousCommonView{buffer1}, ContiguousCommonView{buffer2});
+    auto it = v.begin();
+    assert(it[0] == *it);
+    assert(it[2] == *(it + 2));
+    assert(it[4] == *(it + 4));
+
+    static_assert(std::is_same_v<decltype(it[2]), int&>);
+  }
+
+  {
+    // non random_access_range
+    std::ranges::concat_view v(BidiCommonView{buffer1});
+    auto iter               = v.begin();
+    const auto canSubscript = [](auto&& it) { return requires { it[0]; }; };
+    static_assert(!canSubscript(iter));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.concat/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/size.pass.cpp
new file mode 100644
index 0000000000000..5502c30cef4e4
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/size.pass.cpp
@@ -0,0 +1,139 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// constexpr auto size()
+//     requires(sized_range<_Views> && ...)
+
+// constexpr auto size() const
+//     requires(sized_range<const _Views> && ...)
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+#include "types.h"
+#include "../range_adaptor_types.h"
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+struct View : std::ranges::view_base {
+  std::size_t size_ = 0;
+  constexpr View(std::size_t s) : size_(s) {}
+  constexpr auto begin() const { return buffer; }
+  constexpr auto end() const { return buffer + size_; }
+};
+
+struct SizedNonConst : std::ranges::view_base {
+  using iterator    = forward_iterator<int*>;
+  std::size_t size_ = 0;
+  constexpr SizedNonConst(std::size_t s) : size_(s) {}
+  constexpr auto begin() const { return iterator{buffer}; }
+  constexpr auto end() const { return iterator{buffer + size_}; }
+  constexpr std::size_t size() { return size_; }
+};
+
+struct StrangeSizeView : std::ranges::view_base {
+  constexpr auto begin() const { return buffer; }
+  constexpr auto end() const { return buffer + 8; }
+
+  constexpr auto size() { return 5; }
+  constexpr auto size() const { return 6; }
+};
+
+struct NoSizeView : std::ranges::view_base {
+  constexpr auto begin() const { return buffer; }
+  constexpr auto end() const { return buffer + 8; }
+};
+
+struct IntSizeView : std::ranges::view_base {
+  using iterator = forward_iterator<int*>;
+
+  constexpr IntSizeView() {}
+  constexpr auto begin() const { return iterator{buffer}; }
+  constexpr auto end() const { return iterator{buffer + 9}; }
+  constexpr int size() const { return 9; }
+};
+
+struct UnsignedSizeView : std::ranges::view_base {
+  using iterator = forward_iterator<int*>;
+
+  constexpr UnsignedSizeView() {}
+  constexpr auto begin() const { return iterator{buffer}; }
+  constexpr auto end() const { return iterator{buffer + 9}; }
+  constexpr unsigned int size() const { return 9; }
+};
+
+constexpr bool test() {
+  {
+    // single range
+    std::ranges::concat_view v(View(8));
+    assert(v.size() == 8);
+    assert(std::as_const(v).size() == 8);
+  }
+
+  {
+    // multiple ranges same type
+    std::ranges::concat_view v(View(2), View(3));
+    assert(v.size() == 5);
+    assert(std::as_const(v).size() == 5);
+  }
+
+  {
+    // multiple ranges 
diff erent types
+    std::ranges::concat_view v(std::views::iota(0, 5), View(3));
+    assert(v.size() == 8);
+    assert(std::as_const(v).size() == 8);
+  }
+
+  {
+    // const-view non-sized range
+    std::ranges::concat_view v(SizedNonConst(2), View(3));
+    assert(v.size() == 5);
+    static_assert(std::ranges::sized_range<decltype(v)>);
+    static_assert(!std::ranges::sized_range<decltype(std::as_const(v))>);
+  }
+
+  {
+    // const/non-const has 
diff erent sizes
+    std::ranges::concat_view v(StrangeSizeView{});
+    assert(v.size() == 5);
+    assert(std::as_const(v).size() == 6);
+  }
+
+  {
+    // underlying range not sized
+    std::ranges::concat_view v(InputCommonView{buffer});
+    static_assert(!std::ranges::sized_range<decltype(v)>);
+    static_assert(!std::ranges::sized_range<decltype(std::as_const(v))>);
+  }
+
+  {
+    //two ranges with 
diff erent size type
+    std::ranges::concat_view v(UnsignedSizeView{}, IntSizeView{});
+    assert(v.size() == 18);
+    // common type between size_t and int should be size_t
+    ASSERT_SAME_TYPE(decltype(v.size()), unsigned int);
+  }
+
+  {
+    // three ranges with 
diff erent size type
+    std::ranges::concat_view v(UnsignedSizeView{}, IntSizeView{}, StrangeSizeView{});
+    assert(v.size() == 23);
+    // common type between size_t and int should be size_t
+    ASSERT_SAME_TYPE(decltype(v.size()), unsigned int);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.concat/types.h b/libcxx/test/std/ranges/range.adaptors/range.concat/types.h
new file mode 100644
index 0000000000000..d055f8cfbd8dc
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.concat/types.h
@@ -0,0 +1,72 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_CONCAT_TYPES_H
+#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CONCAT_TYPES_H
+
+#include <ranges>
+#include <utility>
+#include "test_iterators.h"
+
+inline int buff[3] = {1, 2, 3};
+
+template <class Iter, class Sent>
+struct minimal_view : std::ranges::view_base {
+  constexpr explicit minimal_view(Iter it, Sent sent) : it_(base(std::move(it))), sent_(base(std::move(sent))) {}
+
+  minimal_view(minimal_view&&)            = default;
+  minimal_view& operator=(minimal_view&&) = default;
+
+  constexpr Iter begin() const { return Iter(it_); }
+  constexpr Sent end() const { return Sent(sent_); }
+
+private:
+  decltype(base(std::declval<Iter>())) it_;
+  decltype(base(std::declval<Sent>())) sent_;
+};
+
+struct ViewWithNoConstBegin : std::ranges::view_base {
+  int* begin_;
+  int* end_;
+
+  ViewWithNoConstBegin(int* begin, int* end) : begin_(begin), end_(end) {}
+
+  constexpr int* begin() { return begin_; }
+  constexpr int* end() { return end_; }
+};
+
+struct ViewWithConstBegin : std::ranges::view_base {
+  int* begin_;
+  int* end_;
+
+  ViewWithConstBegin(int* begin, int* end) : begin_(begin), end_(end) {}
+
+  constexpr int* begin() { return begin_; }
+  constexpr int* end() { return end_; }
+
+  constexpr int* begin() const { return begin_; }
+  constexpr int* end() const { return end_; }
+};
+
+struct SizedViewWithConstBegin : std::ranges::view_base {
+  int* begin_;
+  int* end_;
+
+  SizedViewWithConstBegin() : begin_(buff), end_(buff + 3) {}
+
+  constexpr int* begin() { return begin_; }
+  constexpr int* end() { return end_; }
+
+  constexpr int* begin() const { return begin_; }
+  constexpr int* end() const { return end_; }
+};
+
+template <class... Views>
+concept ConcatableConstViews = requires(const std::ranges::concat_view<Views...>& cv) { cv.begin(); };
+
+#endif // TEST_STD_RANGES_RANGE_ADAPTORS_CONCAT_FILTER_TYPES_H

diff  --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index f81eb0e0e8060..050dab93bea29 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1125,7 +1125,6 @@ def add_version_header(tc):
             "name": "__cpp_lib_ranges_concat",
             "values": {"c++26": 202403}, # P2542R8: views::concat
             "headers": ["ranges"],
-            "unimplemented": True,
         },
         {
             "name": "__cpp_lib_ranges_contains",


        


More information about the libcxx-commits mailing list