[libcxx-commits] [libcxx] 6a66467 - [libc++] P2770R0: Stashing stashing iterators for proper flattening (#66033)
via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Dec 12 06:45:18 PST 2023
Author: Jakub Mazurkiewicz
Date: 2023-12-12T09:45:14-05:00
New Revision: 6a664674990094c1b5d2e717256f08cb04485899
URL: https://github.com/llvm/llvm-project/commit/6a664674990094c1b5d2e717256f08cb04485899
DIFF: https://github.com/llvm/llvm-project/commit/6a664674990094c1b5d2e717256f08cb04485899.diff
LOG: [libc++] P2770R0: Stashing stashing iterators for proper flattening (#66033)
- Partially implements P2770R0 (http://wg21.link/p2770)
- Fixes https://wg21.link/LWG3698, https://wg21.link/LWG3700, and https://wg21.link/LWG3791
- join_with_view hasn't been done yet since this type isn't implemented yet
- Rename tuple test directory to match the standard (which changed in P2770R0)
- Rename join_view test directory to match the standard
Added:
libcxx/include/__utility/as_lvalue.h
libcxx/test/libcxx/ranges/range.adaptors/range.adaptor.helpers/as-lvalue.lifetimebound.verify.cpp
libcxx/test/libcxx/ranges/range.adaptors/range.adaptor.helpers/as-lvalue.pass.cpp
libcxx/test/libcxx/ranges/range.adaptors/range.adaptor.helpers/tuple-for-each.pass.cpp
libcxx/test/libcxx/ranges/range.adaptors/range.join/range.join.iterator/ctor.parent.outer.pass.cpp
libcxx/test/libcxx/ranges/range.adaptors/range.join/range.join.iterator/ctor.parent.pass.cpp
libcxx/test/libcxx/ranges/range.adaptors/range.join/range.join.iterator/types.h
libcxx/test/std/ranges/range.adaptors/range.join/adaptor.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join/base.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join/begin.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join/ctad.compile.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join/ctad.verify.cpp
libcxx/test/std/ranges/range.adaptors/range.join/ctor.default.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join/ctor.view.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join/end.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join/general.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join/lwg3698.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join/range.join.iterator/arrow.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join/range.join.iterator/ctor.default.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join/range.join.iterator/ctor.other.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join/range.join.iterator/decrement.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join/range.join.iterator/eq.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join/range.join.iterator/increment.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join/range.join.iterator/iter.move.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join/range.join.iterator/iter.swap.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join/range.join.iterator/member_types.compile.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join/range.join.iterator/star.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.default.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.other.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/eq.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join/types.h
Modified:
libcxx/docs/Status/Cxx23.rst
libcxx/docs/Status/Cxx23Papers.csv
libcxx/docs/UsingLibcxx.rst
libcxx/include/CMakeLists.txt
libcxx/include/__ranges/join_view.h
libcxx/include/module.modulemap.in
libcxx/include/regex
libcxx/include/utility
libcxx/modules/std/ranges.inc
libcxx/test/libcxx/ranges/range.adaptors/range.join/segmented_iterator.compile.pass.cpp
libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy.segmented.pass.cpp
libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.pass.cpp
libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.segmented.pass.cpp
libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.segmented.pass.cpp
libcxx/test/std/algorithms/alg.modifying.operations/alg.move/ranges.move.segmented.pass.cpp
libcxx/test/std/algorithms/alg.modifying.operations/alg.move/ranges.move_backward.segmented.pass.cpp
libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
libcxx/test/std/ranges/iterator_robust_against_adl.compile.pass.cpp
libcxx/test/std/re/re.iter/re.regiter/iterator_concept_conformance.compile.pass.cpp
libcxx/test/std/re/re.iter/re.regiter/types.pass.cpp
libcxx/test/std/re/re.iter/re.tokiter/iterator_concept_conformance.compile.pass.cpp
libcxx/test/std/re/re.iter/re.tokiter/types.pass.cpp
Removed:
libcxx/test/libcxx/ranges/range.adaptors/range.adaptor.tuple/tuple-for-each.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join.view/adaptor.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join.view/base.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join.view/begin.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join.view/ctad.compile.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join.view/ctad.verify.cpp
libcxx/test/std/ranges/range.adaptors/range.join.view/ctor.default.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join.view/ctor.view.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join.view/end.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join.view/general.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/arrow.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/ctor.default.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/ctor.other.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/ctor.parent.outer.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/decrement.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/eq.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/increment.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/iter.move.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/iter.swap.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/member_types.compile.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/star.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join.view/sentinel/ctor.default.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join.view/sentinel/ctor.other.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join.view/sentinel/ctor.parent.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join.view/sentinel/eq.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join.view/types.h
################################################################################
diff --git a/libcxx/docs/Status/Cxx23.rst b/libcxx/docs/Status/Cxx23.rst
index 839640a7c7e881..3e6e33f08c7ccf 100644
--- a/libcxx/docs/Status/Cxx23.rst
+++ b/libcxx/docs/Status/Cxx23.rst
@@ -45,6 +45,7 @@ Paper Status
clang doesn't issue a diagnostic for deprecated using template declarations.
.. [#note-P2520R0] P2520R0: Libc++ implemented this paper as a DR in C++20 as well.
.. [#note-P2711R1] P2711R1: ``join_with_view`` hasn't been done yet since this type isn't implemented yet.
+ .. [#note-P2770R0] P2770R0: ``join_with_view`` hasn't been done yet since this type isn't implemented yet.
.. [#note-P2693R1] P2693R1: The formatter for ``std::thread::id`` is implemented.
The formatter for ``stacktrace`` is not implemented, since ``stacktrace`` is
not implemented yet.
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index e03cbff2a08bbf..b4f915d37d0021 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -104,7 +104,7 @@
"`P2708R1 <https://wg21.link/P2708R1>`__","LWG", "No Further Fundamentals TSes", "November 2022","|Nothing to do|","",""
"","","","","","",""
"`P0290R4 <https://wg21.link/P0290R4>`__","LWG", "``apply()`` for ``synchronized_value<T>``","February 2023","","","|concurrency TS|"
-"`P2770R0 <https://wg21.link/P2770R0>`__","LWG", "Stashing stashing ``iterators`` for proper flattening","February 2023","|In Progress|","","|ranges|"
+"`P2770R0 <https://wg21.link/P2770R0>`__","LWG", "Stashing stashing ``iterators`` for proper flattening","February 2023","|Partial| [#note-P2770R0]_","","|ranges|"
"`P2164R9 <https://wg21.link/P2164R9>`__","LWG", "``views::enumerate``","February 2023","","","|ranges|"
"`P2711R1 <https://wg21.link/P2711R1>`__","LWG", "Making multi-param constructors of ``views`` ``explicit``","February 2023","|In Progress| [#note-P2711R1]_","","|ranges|"
"`P2609R3 <https://wg21.link/P2609R3>`__","LWG", "Relaxing Ranges Just A Smidge","February 2023","","","|ranges|"
diff --git a/libcxx/docs/UsingLibcxx.rst b/libcxx/docs/UsingLibcxx.rst
index cdab2c36eab745..8d9f795da977e3 100644
--- a/libcxx/docs/UsingLibcxx.rst
+++ b/libcxx/docs/UsingLibcxx.rst
@@ -50,7 +50,6 @@ when ``-fexperimental-library`` is passed:
* ``std::stop_token``, ``std::stop_source`` and ``std::stop_callback``
* ``std::jthread``
* ``std::chrono::tzdb`` and related time zone functionality
-* ``std::ranges::join_view``
.. warning::
Experimental libraries are experimental.
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index d8faf6467b79ae..533b7ac9103ab0 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -837,6 +837,7 @@ set(files
__type_traits/void_t.h
__undef_macros
__utility/as_const.h
+ __utility/as_lvalue.h
__utility/auto_cast.h
__utility/cmp.h
__utility/convert_to_integral.h
diff --git a/libcxx/include/__ranges/join_view.h b/libcxx/include/__ranges/join_view.h
index e6240dfd2580dc..f80beda33b11ed 100644
--- a/libcxx/include/__ranges/join_view.h
+++ b/libcxx/include/__ranges/join_view.h
@@ -32,6 +32,8 @@
#include <__ranges/view_interface.h>
#include <__type_traits/common_type.h>
#include <__type_traits/maybe_const.h>
+#include <__utility/as_lvalue.h>
+#include <__utility/empty.h>
#include <__utility/forward.h>
#include <optional>
@@ -41,10 +43,7 @@
_LIBCPP_BEGIN_NAMESPACE_STD
-// Note: `join_view` is still marked experimental because there is an ABI-breaking change that affects `join_view` in
-// the pipeline (https://isocpp.org/files/papers/D2770R0.html).
-// TODO: make `join_view` non-experimental once D2770 is implemented.
-#if _LIBCPP_STD_VER >= 20 && defined(_LIBCPP_ENABLE_EXPERIMENTAL)
+#if _LIBCPP_STD_VER >= 20
namespace ranges {
template<class>
@@ -84,11 +83,16 @@ namespace ranges {
template <class>
friend struct std::__segmented_iterator_traits;
- static constexpr bool _UseCache = !is_reference_v<_InnerRange>;
- using _Cache = _If<_UseCache, __non_propagating_cache<remove_cvref_t<_InnerRange>>, __empty_cache>;
- _LIBCPP_NO_UNIQUE_ADDRESS _Cache __cache_;
_LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View();
+ static constexpr bool _UseOuterCache = !forward_range<_View>;
+ using _OuterCache = _If<_UseOuterCache, __non_propagating_cache<iterator_t<_View>>, __empty_cache>;
+ _LIBCPP_NO_UNIQUE_ADDRESS _OuterCache __outer_;
+
+ static constexpr bool _UseInnerCache = !is_reference_v<_InnerRange>;
+ using _InnerCache = _If<_UseInnerCache, __non_propagating_cache<remove_cvref_t<_InnerRange>>, __empty_cache>;
+ _LIBCPP_NO_UNIQUE_ADDRESS _InnerCache __inner_;
+
public:
_LIBCPP_HIDE_FROM_ABI
join_view() requires default_initializable<_View> = default;
@@ -105,16 +109,22 @@ namespace ranges {
_LIBCPP_HIDE_FROM_ABI
constexpr auto begin() {
- constexpr bool __use_const = __simple_view<_View> &&
- is_reference_v<range_reference_t<_View>>;
- return __iterator<__use_const>{*this, ranges::begin(__base_)};
+ if constexpr (forward_range<_View>) {
+ constexpr bool __use_const = __simple_view<_View> &&
+ is_reference_v<range_reference_t<_View>>;
+ return __iterator<__use_const>{*this, ranges::begin(__base_)};
+ } else {
+ __outer_.__emplace(ranges::begin(__base_));
+ return __iterator<false>{*this};
+ }
}
template<class _V2 = _View>
_LIBCPP_HIDE_FROM_ABI
constexpr auto begin() const
- requires input_range<const _V2> &&
- is_reference_v<range_reference_t<const _V2>>
+ requires forward_range<const _V2> &&
+ is_reference_v<range_reference_t<const _V2>> &&
+ input_range<range_reference_t<const _V2>>
{
return __iterator<true>{*this, ranges::begin(__base_)};
}
@@ -134,13 +144,12 @@ namespace ranges {
template<class _V2 = _View>
_LIBCPP_HIDE_FROM_ABI
constexpr auto end() const
- requires input_range<const _V2> &&
- is_reference_v<range_reference_t<const _V2>>
+ requires forward_range<const _V2> &&
+ is_reference_v<range_reference_t<const _V2>> &&
+ input_range<range_reference_t<const _V2>>
{
using _ConstInnerRange = range_reference_t<const _View>;
- if constexpr (forward_range<const _View> &&
- is_reference_v<_ConstInnerRange> &&
- forward_range<_ConstInnerRange> &&
+ if constexpr (forward_range<_ConstInnerRange> &&
common_range<const _View> &&
common_range<_ConstInnerRange>) {
return __iterator<true>{*this, ranges::end(__base_)};
@@ -154,12 +163,12 @@ namespace ranges {
requires view<_View> && input_range<range_reference_t<_View>>
template<bool _Const>
struct join_view<_View>::__sentinel {
- template<bool>
+ private:
+ template <bool>
friend struct __sentinel;
- private:
- using _Parent = __maybe_const<_Const, join_view<_View>>;
- using _Base = __maybe_const<_Const, _View>;
+ using _Parent = __maybe_const<_Const, join_view>;
+ using _Base = __maybe_const<_Const, _View>;
sentinel_t<_Base> __end_ = sentinel_t<_Base>();
public:
@@ -179,7 +188,7 @@ namespace ranges {
requires sentinel_for<sentinel_t<_Base>, iterator_t<__maybe_const<_OtherConst, _View>>>
_LIBCPP_HIDE_FROM_ABI
friend constexpr bool operator==(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
- return __x.__outer_ == __y.__end_;
+ return __x.__get_outer() == __y.__end_;
}
};
@@ -191,9 +200,7 @@ namespace ranges {
template<bool _Const>
struct join_view<_View>::__iterator final
: public __join_view_iterator_category<__maybe_const<_Const, _View>> {
-
- template<bool>
- friend struct __iterator;
+ friend join_view;
template <class>
friend struct std::__segmented_iterator_traits;
@@ -207,23 +214,25 @@ namespace ranges {
using _Inner = iterator_t<range_reference_t<_Base>>;
using _InnerRange = range_reference_t<_View>;
+ static_assert(!_Const || forward_range<_Base>, "Const can only be true when Base models forward_range.");
+
static constexpr bool __ref_is_glvalue = is_reference_v<range_reference_t<_Base>>;
- public:
- _Outer __outer_ = _Outer();
+ static constexpr bool _OuterPresent = forward_range<_Base>;
+ using _OuterType = _If<_OuterPresent, _Outer, std::__empty>;
+ _LIBCPP_NO_UNIQUE_ADDRESS _OuterType __outer_ = _OuterType();
- private:
optional<_Inner> __inner_;
- _Parent *__parent_ = nullptr;
+ _Parent* __parent_ = nullptr;
_LIBCPP_HIDE_FROM_ABI
constexpr void __satisfy() {
- for (; __outer_ != ranges::end(__parent_->__base_); ++__outer_) {
- auto&& __inner = [&]() -> auto&& {
+ for (; __get_outer() != ranges::end(__parent_->__base_); ++__get_outer()) {
+ auto&& __inner = [this]() -> auto&& {
if constexpr (__ref_is_glvalue)
- return *__outer_;
+ return *__get_outer();
else
- return __parent_->__cache_.__emplace_from([&]() -> decltype(auto) { return *__outer_; });
+ return __parent_->__inner_.__emplace_from([&]() -> decltype(auto) { return *__get_outer(); });
}();
__inner_ = ranges::begin(__inner);
if (*__inner_ != ranges::end(__inner))
@@ -234,8 +243,37 @@ namespace ranges {
__inner_.reset();
}
+ _LIBCPP_HIDE_FROM_ABI constexpr _Outer& __get_outer() {
+ if constexpr (forward_range<_Base>) {
+ return __outer_;
+ } else {
+ return *__parent_->__outer_;
+ }
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr const _Outer& __get_outer() const {
+ if constexpr (forward_range<_Base>) {
+ return __outer_;
+ } else {
+ return *__parent_->__outer_;
+ }
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, _Outer __outer)
+ requires forward_range<_Base>
+ : __outer_(std::move(__outer)), __parent_(std::addressof(__parent)) {
+ __satisfy();
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(_Parent& __parent)
+ requires(!forward_range<_Base>)
+ : __parent_(std::addressof(__parent)) {
+ __satisfy();
+ }
+
_LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent* __parent, _Outer __outer, _Inner __inner)
- : __outer_(std::move(__outer)), __inner_(std::move(__inner)), __parent_(__parent) {}
+ requires forward_range<_Base>
+ : __outer_(std::move(__outer)), __inner_(std::move(__inner)), __parent_(__parent) {}
public:
using iterator_concept = _If<
@@ -254,15 +292,7 @@ namespace ranges {
using
diff erence_type = common_type_t<
range_
diff erence_t<_Base>, range_
diff erence_t<range_reference_t<_Base>>>;
- _LIBCPP_HIDE_FROM_ABI
- __iterator() requires default_initializable<_Outer> = default;
-
- _LIBCPP_HIDE_FROM_ABI
- constexpr __iterator(_Parent& __parent, _Outer __outer)
- : __outer_(std::move(__outer))
- , __parent_(std::addressof(__parent)) {
- __satisfy();
- }
+ _LIBCPP_HIDE_FROM_ABI __iterator() = default;
_LIBCPP_HIDE_FROM_ABI
constexpr __iterator(__iterator<!_Const> __i)
@@ -287,14 +317,14 @@ namespace ranges {
_LIBCPP_HIDE_FROM_ABI
constexpr __iterator& operator++() {
- auto&& __inner = [&]() -> auto&& {
+ auto __get_inner_range = [&]() -> decltype(auto) {
if constexpr (__ref_is_glvalue)
- return *__outer_;
+ return *__get_outer();
else
- return *__parent_->__cache_;
- }();
- if (++*__inner_ == ranges::end(__inner)) {
- ++__outer_;
+ return *__parent_->__inner_;
+ };
+ if (++*__inner_ == ranges::end(std::__as_lvalue(__get_inner_range()))) {
+ ++__get_outer();
__satisfy();
}
return *this;
@@ -324,11 +354,11 @@ namespace ranges {
common_range<range_reference_t<_Base>>
{
if (__outer_ == ranges::end(__parent_->__base_))
- __inner_ = ranges::end(*--__outer_);
+ __inner_ = ranges::end(std::__as_lvalue(*--__outer_));
// Skip empty inner ranges when going backwards.
- while (*__inner_ == ranges::begin(*__outer_)) {
- __inner_ = ranges::end(*--__outer_);
+ while (*__inner_ == ranges::begin(std::__as_lvalue(*__outer_))) {
+ __inner_ = ranges::end(std::__as_lvalue(*--__outer_));
}
--*__inner_;
@@ -350,7 +380,7 @@ namespace ranges {
_LIBCPP_HIDE_FROM_ABI
friend constexpr bool operator==(const __iterator& __x, const __iterator& __y)
requires __ref_is_glvalue &&
- equality_comparable<iterator_t<_Base>> &&
+ forward_range<_Base> &&
equality_comparable<iterator_t<range_reference_t<_Base>>>
{
return __x.__outer_ == __y.__outer_ && __x.__inner_ == __y.__inner_;
@@ -436,7 +466,7 @@ struct __segmented_iterator_traits<_JoinViewIterator> {
}
};
-#endif // #if _LIBCPP_STD_VER >= 20 && defined(_LIBCPP_ENABLE_EXPERIMENTAL)
+#endif // #if _LIBCPP_STD_VER >= 20
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__utility/as_lvalue.h b/libcxx/include/__utility/as_lvalue.h
new file mode 100644
index 00000000000000..159f45dad4d41c
--- /dev/null
+++ b/libcxx/include/__utility/as_lvalue.h
@@ -0,0 +1,37 @@
+// -*- 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___UTILITY_AS_LVALUE_H
+#define _LIBCPP___UTILITY_AS_LVALUE_H
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#ifndef _LIBCPP_CXX03_LANG
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr _Tp& __as_lvalue(_LIBCPP_LIFETIMEBOUND _Tp&& __t) {
+ return static_cast<_Tp&>(__t);
+}
+
+#endif // !_LIBCPP_CXX03_LANG
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___UTILITY_AS_LVALUE_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 90ee7fbb2157c2..7e93e0a155033d 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2019,6 +2019,7 @@ module std_private_type_traits_unwrap_ref [system
module std_private_type_traits_void_t [system] { header "__type_traits/void_t.h" }
module std_private_utility_as_const [system] { header "__utility/as_const.h" }
+module std_private_utility_as_lvalue [system] { header "__utility/as_lvalue.h" }
module std_private_utility_auto_cast [system] {
header "__utility/auto_cast.h"
export std_private_type_traits_decay
diff --git a/libcxx/include/regex b/libcxx/include/regex
index fcdd85f8c4997e..008fe70a0ca6e7 100644
--- a/libcxx/include/regex
+++ b/libcxx/include/regex
@@ -697,6 +697,7 @@ public:
typedef const value_type* pointer;
typedef const value_type& reference;
typedef forward_iterator_tag iterator_category;
+ typedef input_iterator_tag iterator_concept; // since C++20
regex_iterator();
regex_iterator(BidirectionalIterator a, BidirectionalIterator b,
@@ -737,6 +738,7 @@ public:
typedef const value_type* pointer;
typedef const value_type& reference;
typedef forward_iterator_tag iterator_category;
+ typedef input_iterator_tag iterator_concept; // since C++20
regex_token_iterator();
regex_token_iterator(BidirectionalIterator a, BidirectionalIterator b,
@@ -6407,6 +6409,9 @@ public:
typedef const value_type* pointer;
typedef const value_type& reference;
typedef forward_iterator_tag iterator_category;
+#if _LIBCPP_STD_VER >= 20
+ typedef input_iterator_tag iterator_concept;
+#endif
private:
_BidirectionalIterator __begin_;
@@ -6542,6 +6547,9 @@ public:
typedef const value_type* pointer;
typedef const value_type& reference;
typedef forward_iterator_tag iterator_category;
+#if _LIBCPP_STD_VER >= 20
+ typedef input_iterator_tag iterator_concept;
+#endif
private:
typedef regex_iterator<_BidirectionalIterator, _CharT, _Traits> _Position;
diff --git a/libcxx/include/utility b/libcxx/include/utility
index c5581d55e79bbb..1deef3db204107 100644
--- a/libcxx/include/utility
+++ b/libcxx/include/utility
@@ -249,6 +249,7 @@ template <class T>
#include <__assert> // all public C++ headers provide the assertion handler
#include <__config>
#include <__utility/as_const.h>
+#include <__utility/as_lvalue.h>
#include <__utility/auto_cast.h>
#include <__utility/cmp.h>
#include <__utility/declval.h>
diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc
index a883103d812588..82c7d99f8979a8 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -204,13 +204,11 @@ export namespace std {
using std::ranges::views::drop_while;
} // namespace views
-#ifdef _LIBCPP_ENABLE_EXPERIMENTAL
using std::ranges::join_view;
namespace views {
using std::ranges::views::join;
} // namespace views
-#endif // _LIBCPP_ENABLE_EXPERIMENTAL
#if 0
using std::ranges::join_with_view;
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adaptor.helpers/as-lvalue.lifetimebound.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adaptor.helpers/as-lvalue.lifetimebound.verify.cpp
new file mode 100644
index 00000000000000..7046936b1b7a7f
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adaptor.helpers/as-lvalue.lifetimebound.verify.cpp
@@ -0,0 +1,22 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03
+
+// template<class T>
+// constexpr T& as-lvalue(T&& t) { // exposition only
+
+#include <utility>
+
+void test() {
+ // Check prvalue
+ {
+ [[maybe_unused]] auto& check = std::__as_lvalue(
+ 0); // expected-warning {{temporary bound to local reference 'check' will be destroyed at the end of the full-expression}}
+ }
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adaptor.helpers/as-lvalue.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adaptor.helpers/as-lvalue.pass.cpp
new file mode 100644
index 00000000000000..721279fcd586b0
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adaptor.helpers/as-lvalue.pass.cpp
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11
+
+// template<class T>
+// constexpr T& as-lvalue(T&& t) { // exposition only
+
+#include <type_traits>
+#include <utility>
+
+constexpr bool test() {
+ // Check glvalue
+ {
+ int lvalue{};
+ [[maybe_unused]] decltype(auto) check = std::__as_lvalue(lvalue);
+ static_assert(std::is_same<decltype(check), int&>::value, "");
+ }
+
+ // Check xvalue
+ {
+ int xvalue{};
+ [[maybe_unused]] decltype(auto) check = std::__as_lvalue(std::move(xvalue));
+ static_assert(std::is_same<decltype(check), int&>::value, "");
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test(), "");
+
+ return 0;
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adaptor.tuple/tuple-for-each.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adaptor.helpers/tuple-for-each.pass.cpp
similarity index 100%
rename from libcxx/test/libcxx/ranges/range.adaptors/range.adaptor.tuple/tuple-for-each.pass.cpp
rename to libcxx/test/libcxx/ranges/range.adaptors/range.adaptor.helpers/tuple-for-each.pass.cpp
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join/range.join.iterator/ctor.parent.outer.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join/range.join.iterator/ctor.parent.outer.pass.cpp
new file mode 100644
index 00000000000000..a54980bec287fc
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join/range.join.iterator/ctor.parent.outer.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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// constexpr iterator(Parent& parent, OuterIter outer)
+// requires forward_range<Base>; // exposition only
+
+#include <cassert>
+#include <ranges>
+#include <string>
+#include <utility>
+
+#include "types.h"
+
+constexpr bool test() {
+ std::string strings[4] = {"aaaa", "bbbb", "cccc", "dddd"};
+
+ { // Check if `outer_` is initialized with `std::move(outer)` for `iterator<false>`
+ MoveOnAccessSubrange r{DieOnCopyIterator(strings), sentinel_wrapper(strings + 4)};
+ std::ranges::join_view jv(std::move(r));
+ auto iter = jv.begin(); // Calls `iterator(Parent& parent, OuterIter outer)`
+ assert(*iter == 'a');
+ }
+
+ { // Check if `outer_` is initialized with `std::move(outer)` for `iterator<true>`
+ MoveOnAccessSubrange r{DieOnCopyIterator(strings), sentinel_wrapper(strings + 4)};
+ std::ranges::join_view jv(std::ranges::ref_view{r});
+ auto iter = std::as_const(jv).begin(); // Calls `iterator(Parent& parent, OuterIter outer)`
+ assert(*iter == 'a');
+ }
+
+ {
+ // LWG3569 Inner iterator not default_initializable
+ // With the current spec, the constructor under test invokes Inner iterator's default constructor
+ // even if it is not default constructible.
+ // This test is checking that this constructor can be invoked with an inner range with non default
+ // constructible iterator.
+ using NonDefaultCtrIter = cpp20_input_iterator<int*>;
+ static_assert(!std::default_initializable<NonDefaultCtrIter>);
+ using NonDefaultCtrIterView = BufferView<NonDefaultCtrIter, sentinel_wrapper<NonDefaultCtrIter>>;
+ static_assert(std::ranges::input_range<NonDefaultCtrIterView>);
+
+ int buffer[2][2] = {{1, 2}, {3, 4}};
+ NonDefaultCtrIterView inners[] = {buffer[0], buffer[1]};
+ auto outer = std::views::all(inners);
+ std::ranges::join_view jv(outer);
+ auto iter = jv.begin(); // Calls `iterator(Parent& parent, OuterIter outer)`
+ assert(*iter == 1);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join/range.join.iterator/ctor.parent.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join/range.join.iterator/ctor.parent.pass.cpp
new file mode 100644
index 00000000000000..3026a02abf00f3
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join/range.join.iterator/ctor.parent.pass.cpp
@@ -0,0 +1,36 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// constexpr explicit iterator(Parent& parent)
+// requires (!forward_range<Base>); // exposition only
+
+#include <string>
+#include <ranges>
+
+#include "types.h"
+
+constexpr bool test() {
+ std::string strings[4] = {"eeee", "ffff", "gggg", "hhhh"};
+
+ MoveOnAccessSubrange r{
+ DieOnCopyIterator(cpp20_input_iterator(strings)), sentinel_wrapper(cpp20_input_iterator(strings + 4))};
+ std::ranges::join_view jv(std::move(r));
+ auto iter = jv.begin(); // Calls `iterator(Parent& parent)`
+ assert(*iter == 'e');
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join/range.join.iterator/types.h b/libcxx/test/libcxx/ranges/range.adaptors/range.join/range.join.iterator/types.h
new file mode 100644
index 00000000000000..0652d4bdb4717c
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join/range.join.iterator/types.h
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_JOIN_RANGE_JOIN_ITERATOR_TYPES_H
+#define TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_JOIN_RANGE_JOIN_ITERATOR_TYPES_H
+
+#include <cassert>
+#include <cstddef>
+#include <ranges>
+
+#include "test_iterators.h"
+
+template <std::input_iterator Iter>
+struct DieOnCopyIterator {
+ using value_type = std::iter_value_t<Iter>;
+ using
diff erence_type = std::iter_
diff erence_t<Iter>;
+
+ DieOnCopyIterator()
+ requires std::default_initializable<Iter>
+ = default;
+
+ constexpr explicit DieOnCopyIterator(Iter iter) : iter_(std::move(iter)) {}
+ constexpr DieOnCopyIterator(DieOnCopyIterator&& other) = default;
+ DieOnCopyIterator& operator=(DieOnCopyIterator&&) = default;
+
+ constexpr DieOnCopyIterator(const DieOnCopyIterator&) { assert(false); }
+ constexpr DieOnCopyIterator& operator=(const DieOnCopyIterator&) { assert(false); }
+
+ constexpr DieOnCopyIterator& operator++() {
+ ++iter_;
+ return *this;
+ }
+
+ constexpr void operator++(int) { iter_++; }
+
+ constexpr DieOnCopyIterator operator++(int)
+ requires std::forward_iterator<Iter>
+ {
+ auto tmp = *this;
+ ++tmp;
+ return tmp;
+ }
+
+ constexpr decltype(auto) operator*() const { return *iter_; }
+
+ friend constexpr bool operator==(const DieOnCopyIterator& left, const DieOnCopyIterator& right)
+ requires std::equality_comparable<Iter>
+ {
+ return left.iter_ == right.iter_;
+ }
+
+ friend constexpr bool operator==(const DieOnCopyIterator& it, const sentinel_wrapper<Iter>& se) {
+ return it.iter_ == se;
+ }
+
+private:
+ Iter iter_ = Iter();
+};
+
+template <class Iter>
+explicit DieOnCopyIterator(Iter) -> DieOnCopyIterator<Iter>;
+
+static_assert(std::input_iterator<DieOnCopyIterator<cpp20_input_iterator<int*>>>);
+static_assert(!std::forward_iterator<DieOnCopyIterator<cpp20_input_iterator<int*>>>);
+static_assert(std::forward_iterator<DieOnCopyIterator<int*>>);
+static_assert(!std::bidirectional_iterator<DieOnCopyIterator<int*>>);
+static_assert(std::sentinel_for<sentinel_wrapper<int*>, DieOnCopyIterator<int*>>);
+
+template <std::input_iterator Iter, std::sentinel_for<Iter> Sent = Iter>
+struct MoveOnAccessSubrange : std::ranges::view_base {
+ constexpr explicit MoveOnAccessSubrange(Iter iter, Sent sent) : iter_(std::move(iter)), sent_(std::move(sent)) {}
+
+ MoveOnAccessSubrange(MoveOnAccessSubrange&&) = default;
+ MoveOnAccessSubrange& operator=(MoveOnAccessSubrange&&) = default;
+
+ MoveOnAccessSubrange(const MoveOnAccessSubrange&) = delete;
+ MoveOnAccessSubrange& operator=(const MoveOnAccessSubrange&) = delete;
+
+ constexpr Iter begin() { return std::move(iter_); }
+ constexpr Sent end() { return std::move(sent_); }
+
+private:
+ Iter iter_;
+ Sent sent_;
+};
+
+template <class Iter, class Sent>
+MoveOnAccessSubrange(Iter, Sent) -> MoveOnAccessSubrange<Iter, Sent>;
+
+static_assert(std::ranges::input_range<MoveOnAccessSubrange<int*, sentinel_wrapper<int*>>>);
+static_assert(std::ranges::forward_range<MoveOnAccessSubrange<DieOnCopyIterator<int*>>>);
+
+template <class Iter, class Sent>
+ requires(!std::same_as<Iter, Sent>)
+struct BufferView : std::ranges::view_base {
+ using T = std::iter_value_t<Iter>;
+ T* data_;
+ std::size_t size_;
+
+ template <std::size_t N>
+ constexpr BufferView(T (&b)[N]) : data_(b), size_(N) {}
+
+ constexpr Iter begin() const { return Iter(data_); }
+ constexpr Sent end() const { return Sent(Iter(data_ + size_)); }
+};
+
+static_assert(std::ranges::input_range<BufferView<int*, sentinel_wrapper<int*>>>);
+
+#endif // TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_JOIN_RANGE_JOIN_ITERATOR_TYPES_H
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join/segmented_iterator.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join/segmented_iterator.compile.pass.cpp
index 82e8cab503a275..6cd17c2b3f3533 100644
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.join/segmented_iterator.compile.pass.cpp
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join/segmented_iterator.compile.pass.cpp
@@ -7,7 +7,6 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
-// UNSUPPORTED: !c++experimental
#include <ranges>
#include <utility>
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy.segmented.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy.segmented.pass.cpp
index 50fb479afcd064..ec60ab8db1609b 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy.segmented.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy.segmented.pass.cpp
@@ -7,8 +7,6 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
-// TODO: make `join_view` non-experimental once D2770 is implemented.
-// UNSUPPORTED: !c++experimental
#include <algorithm>
#include <array>
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.pass.cpp
index feca8fd3be858b..343447446ab2d7 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.pass.cpp
@@ -9,8 +9,6 @@
// <algorithm>
// UNSUPPORTED: c++03, c++11, c++14, c++17
-// TODO: make `join_view` non-experimental once D2770 is implemented.
-// UNSUPPORTED: !c++experimental
// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
// template<bidirectional_iterator I1, sentinel_for<I1> S1, bidirectional_iterator I2>
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.segmented.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.segmented.pass.cpp
index c434cea1208cfe..efeada57625581 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.segmented.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.segmented.pass.cpp
@@ -7,8 +7,6 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
-// TODO: make `join_view` non-experimental once D2770 is implemented.
-// UNSUPPORTED: !c++experimental
#include <algorithm>
#include <array>
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.segmented.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.segmented.pass.cpp
index eae40cefa663f8..7da0f30775905f 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.segmented.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.segmented.pass.cpp
@@ -7,8 +7,6 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
-// TODO: make `join_view` non-experimental once D2770 is implemented.
-// UNSUPPORTED: !c++experimental
#include <algorithm>
#include <array>
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/ranges.move.segmented.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/ranges.move.segmented.pass.cpp
index 2df6a10b18504c..e29ba8af07d6f8 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/ranges.move.segmented.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/ranges.move.segmented.pass.cpp
@@ -7,8 +7,6 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
-// TODO: make `join_view` non-experimental once D2770 is implemented.
-// UNSUPPORTED: !c++experimental
#include <algorithm>
#include <array>
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/ranges.move_backward.segmented.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/ranges.move_backward.segmented.pass.cpp
index 0f0a71439a10dd..50f371a6d64d3a 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/ranges.move_backward.segmented.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/ranges.move_backward.segmented.pass.cpp
@@ -7,8 +7,6 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
-// TODO: make `join_view` non-experimental once D2770 is implemented.
-// UNSUPPORTED: !c++experimental
#include <algorithm>
#include <array>
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 e6c0e09dfff5f8..060f179fe16832 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
@@ -7,8 +7,6 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
-// TODO: make `join_view` non-experimental once D2770 is implemented.
-// UNSUPPORTED: !c++experimental
// [customization.point.object]
// [range.adaptor.object] "A range adaptor object is a customization point object..."
diff --git a/libcxx/test/std/ranges/iterator_robust_against_adl.compile.pass.cpp b/libcxx/test/std/ranges/iterator_robust_against_adl.compile.pass.cpp
index 09b77c0901a229..5efd6c72a13dbf 100644
--- a/libcxx/test/std/ranges/iterator_robust_against_adl.compile.pass.cpp
+++ b/libcxx/test/std/ranges/iterator_robust_against_adl.compile.pass.cpp
@@ -7,7 +7,6 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
-// UNSUPPORTED: !c++experimental
// ADL call with nested iterators of views should not look up base's view's
// namespace
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/ctor.parent.outer.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/ctor.parent.outer.pass.cpp
deleted file mode 100644
index 215318f15cad0d..00000000000000
--- a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/ctor.parent.outer.pass.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-// UNSUPPORTED: c++03, c++11, c++14, c++17
-// UNSUPPORTED: !c++experimental
-
-// constexpr iterator(Parent& parent, OuterIter outer);
-
-#include <cassert>
-#include <ranges>
-
-#include "../types.h"
-
-using NonDefaultCtrIter = cpp20_input_iterator<int*>;
-static_assert(!std::default_initializable<NonDefaultCtrIter>);
-
-using NonDefaultCtrIterView = BufferView<NonDefaultCtrIter, sentinel_wrapper<NonDefaultCtrIter>>;
-static_assert(std::ranges::input_range<NonDefaultCtrIterView>);
-
-constexpr bool test() {
- int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}};
- {
- CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]),
- CopyableChild(buffer[3])};
- CopyableParent parent{children};
- std::ranges::join_view jv(parent);
- std::ranges::iterator_t<decltype(jv)> iter(jv, std::ranges::begin(parent));
- assert(*iter == 1);
- }
-
- {
- // LWG3569 Inner iterator not default_initializable
- // With the current spec, the constructor under test invokes Inner iterator's default constructor
- // even if it is not default constructible
- // This test is checking that this constructor can be invoked with an inner range with non default
- // constructible iterator
- NonDefaultCtrIterView inners[] = {buffer[0], buffer[1]};
- auto outer = std::views::all(inners);
- std::ranges::join_view jv(outer);
- std::ranges::iterator_t<decltype(jv)> iter(jv, std::ranges::begin(outer));
- assert(*iter == 1);
- }
-
- return true;
-}
-
-int main(int, char**) {
- test();
- static_assert(test());
-
- return 0;
-}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/adaptor.pass.cpp
similarity index 99%
rename from libcxx/test/std/ranges/range.adaptors/range.join.view/adaptor.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.join/adaptor.pass.cpp
index afaf3227210996..9beb3d282a27cc 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.view/adaptor.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.join/adaptor.pass.cpp
@@ -7,7 +7,6 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
-// UNSUPPORTED: !c++experimental
// std::views::join
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/base.pass.cpp
similarity index 98%
rename from libcxx/test/std/ranges/range.adaptors/range.join.view/base.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.join/base.pass.cpp
index 13883e894ac7bf..caf018b582263a 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.view/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.join/base.pass.cpp
@@ -7,7 +7,6 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
-// UNSUPPORTED: !c++experimental
// constexpr V base() const& requires copy_constructible<V>;
// constexpr V base() &&;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/begin.pass.cpp
similarity index 83%
rename from libcxx/test/std/ranges/range.adaptors/range.join.view/begin.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.join/begin.pass.cpp
index 9e4fa5f8c59a44..005d0d1d2d5cb7 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.view/begin.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.join/begin.pass.cpp
@@ -7,15 +7,17 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
-// UNSUPPORTED: !c++experimental
// constexpr auto begin();
// constexpr auto begin() const
-// requires input_range<const V> &&
-// is_reference_v<range_reference_t<const V>>;
+// requires forward_range<const V> &&
+// is_reference_v<range_reference_t<const V>> &&
+// input_range<range_reference_t<const V>>;
+#include <algorithm>
#include <cassert>
#include <ranges>
+#include <string_view>
#include "types.h"
@@ -120,7 +122,7 @@ constexpr bool test() {
static_assert(HasConstBegin<decltype(jv)>);
}
- // !input_range<const V>
+ // !forward_range<const V>
{
std::ranges::join_view jv{ConstNotRange{}};
static_assert(!HasConstBegin<decltype(jv)>);
@@ -146,6 +148,27 @@ constexpr bool test() {
static_assert(std::same_as<decltype(jv.begin()), decltype(std::as_const(jv).begin())>);
}
+ // Check stashing iterators (LWG3698: regex_iterator and join_view don't work together very well)
+ {
+ std::ranges::join_view<StashingRange> jv;
+ assert(std::ranges::equal(std::views::counted(jv.begin(), 10), std::string_view{"aababcabcd"}));
+ }
+
+ // LWG3700: The `const begin` of the `join_view` family does not require `InnerRng` to be a range
+ {
+ std::ranges::join_view<ConstNonJoinableRange> jv;
+ static_assert(!HasConstBegin<decltype(jv)>);
+ }
+
+ // Check example from LWG3700
+ {
+ auto r = std::views::iota(0, 5) | std::views::split(1);
+ auto s = std::views::single(r);
+ auto j = s | std::views::join;
+ auto f = j.front();
+ assert(std::ranges::equal(f, std::views::single(0)));
+ }
+
return true;
}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/ctad.compile.pass.cpp
similarity index 98%
rename from libcxx/test/std/ranges/range.adaptors/range.join.view/ctad.compile.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.join/ctad.compile.pass.cpp
index 2c470991be0b6b..a8eafc5a9c0211 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.view/ctad.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.join/ctad.compile.pass.cpp
@@ -7,7 +7,6 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
-// UNSUPPORTED: !c++experimental
// template<class R>
// explicit join_view(R&&) -> join_view<views::all_t<R>>;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/ctad.verify.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/ctad.verify.cpp
similarity index 97%
rename from libcxx/test/std/ranges/range.adaptors/range.join.view/ctad.verify.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.join/ctad.verify.cpp
index eddc950747ba76..2c6eea500580d6 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.view/ctad.verify.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.join/ctad.verify.cpp
@@ -7,7 +7,6 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
-// UNSUPPORTED: !c++experimental
// template<class R>
// explicit join_view(R&&) -> join_view<views::all_t<R>>;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/ctor.default.pass.cpp
similarity index 97%
rename from libcxx/test/std/ranges/range.adaptors/range.join.view/ctor.default.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.join/ctor.default.pass.cpp
index 26206e32c358ce..0daff7d3b3c98a 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.view/ctor.default.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.join/ctor.default.pass.cpp
@@ -7,7 +7,6 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
-// UNSUPPORTED: !c++experimental
// join_view() requires default_initializable<V> = default;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/ctor.view.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/ctor.view.pass.cpp
similarity index 97%
rename from libcxx/test/std/ranges/range.adaptors/range.join.view/ctor.view.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.join/ctor.view.pass.cpp
index ce5393062d7781..75d4c7e5916b0a 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.view/ctor.view.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.join/ctor.view.pass.cpp
@@ -7,7 +7,6 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
-// UNSUPPORTED: !c++experimental
// constexpr explicit join_view(V base);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/end.pass.cpp
similarity index 95%
rename from libcxx/test/std/ranges/range.adaptors/range.join.view/end.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.join/end.pass.cpp
index 7e225202cc2312..516ba25a0e8596 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.view/end.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.join/end.pass.cpp
@@ -7,10 +7,12 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
-// UNSUPPORTED: !c++experimental
// constexpr auto end();
// constexpr auto end() const;
+// requires forward_range<const V> &&
+// is_reference_v<range_reference_t<const V>> &&
+// input_range<range_reference_t<const V>>
#include <cassert>
#include <ranges>
@@ -33,13 +35,13 @@ concept HasConstEnd = requires (const T& t){
// | 3 | Y | Y | Y | Y | N | Y |sentinel<true> |sentinel<true>|
// | 4 | Y | Y | Y | N | Y | Y |sentinel<true> | - |
// | 5 | Y | Y | N | Y | Y | Y |sentinel<true> |sentinel<true>|
-// | 6 | Y | N | Y | Y | Y | Y |sentinel<true> |sentinel<true>|
+// | 6 | Y | N | Y | Y | Y | Y |sentinel<true> | - |
// | 7 | N | Y | Y | Y | Y | Y |iterator<false>|iterator<true>|
// | 8 | N | Y | Y | Y | Y | N |sentinel<false>|sentinel<true>|
// | 9 | N | Y | Y | Y | N | Y |sentinel<false>|sentinel<true>|
// | 10 | N | Y | Y | N | Y | Y |sentinel<false>| - |
// | 11 | N | Y | N | Y | Y | Y |sentinel<false>|sentinel<true>|
-// | 12 | N | N | Y | Y | Y | Y |sentinel<false>|sentinel<true>|
+// | 12 | N | N | Y | Y | Y | Y |sentinel<false>| - |
//
//
@@ -131,10 +133,8 @@ constexpr bool test() {
std::ranges::join_view jv(outer);
assert(jv.end() == std::ranges::next(jv.begin(), 16));
- assert(std::as_const(jv).end() == std::ranges::next(std::as_const(jv).begin(), 16));
- static_assert(HasConstEnd<decltype(jv)>);
- static_assert(std::same_as<decltype(jv.end()), decltype(std::as_const(jv).end())>);
+ static_assert(!HasConstEnd<decltype(jv)>);
static_assert(!std::ranges::common_range<decltype(jv)>);
static_assert(!std::ranges::common_range<const decltype(jv)>);
}
@@ -219,10 +219,8 @@ constexpr bool test() {
std::ranges::join_view jv(outer);
assert(jv.end() == std::ranges::next(jv.begin(), 16));
- assert(std::as_const(jv).end() == std::ranges::next(std::as_const(jv).begin(), 16));
- static_assert(HasConstEnd<decltype(jv)>);
- static_assert(!std::same_as<decltype(jv.end()), decltype(std::as_const(jv).end())>);
+ static_assert(!HasConstEnd<decltype(jv)>);
static_assert(!std::ranges::common_range<decltype(jv)>);
static_assert(!std::ranges::common_range<const decltype(jv)>);
}
@@ -288,6 +286,12 @@ constexpr bool test() {
assert(jv.end() == std::ranges::next(jv.begin(), 12));
}
+ // LWG3700: The `const begin` of the `join_view` family does not require `InnerRng` to be a range
+ {
+ std::ranges::join_view<ConstNonJoinableRange> jv;
+ static_assert(!HasConstEnd<decltype(jv)>);
+ }
+
return true;
}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/general.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/general.pass.cpp
similarity index 98%
rename from libcxx/test/std/ranges/range.adaptors/range.join.view/general.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.join/general.pass.cpp
index e9eab585260cdc..f92eb418fac77c 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.view/general.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.join/general.pass.cpp
@@ -7,7 +7,6 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
-// UNSUPPORTED: !c++experimental
// General tests for join_view. This file does not test anything specifically.
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join/lwg3698.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/lwg3698.pass.cpp
new file mode 100644
index 00000000000000..0abe37bf17f7e8
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join/lwg3698.pass.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-localization
+
+// Check LWG-3698: `regex_iterator` and `join_view` don't work together very well
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <ranges>
+#include <regex>
+#include <string_view>
+
+int main(int, char**) {
+ char const text[] = "Hello";
+ std::regex regex{"[a-z]"};
+
+ auto lower =
+ std::ranges::subrange(
+ std::cregex_iterator(std::ranges::begin(text), std::ranges::end(text), regex), std::cregex_iterator{}) |
+ std::views::join | std::views::transform([](auto const& sm) { return std::string_view(sm.first, sm.second); });
+
+ assert(std::ranges::equal(lower, std::to_array<std::string_view>({"e", "l", "l", "o"})));
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/arrow.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.iterator/arrow.pass.cpp
similarity index 99%
rename from libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/arrow.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.join/range.join.iterator/arrow.pass.cpp
index e610cde2c3b5be..ddcf66bfe775e7 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/arrow.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.iterator/arrow.pass.cpp
@@ -7,7 +7,6 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
-// UNSUPPORTED: !c++experimental
// constexpr InnerIter operator->() const
// requires has-arrow<InnerIter> && copyable<InnerIter>;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.iterator/ctor.default.pass.cpp
similarity index 70%
rename from libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/ctor.default.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.join/range.join.iterator/ctor.default.pass.cpp
index e4f193e4e60642..82fe824fad1b2a 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/ctor.default.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.iterator/ctor.default.pass.cpp
@@ -7,9 +7,8 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
-// UNSUPPORTED: !c++experimental
-// iterator() requires default_initializable<OuterIter> = default;
+// iterator() = default;
#include <ranges>
@@ -29,19 +28,12 @@ constexpr void test_default_constructible() {
using JoinView = std::ranges::join_view<view<It>>;
using JoinIterator = std::ranges::iterator_t<JoinView>;
static_assert(std::is_default_constructible_v<JoinIterator>);
- JoinIterator it; (void)it;
-}
-
-template <class It>
-constexpr void test_non_default_constructible() {
- using JoinView = std::ranges::join_view<view<It>>;
- using JoinIterator = std::ranges::iterator_t<JoinView>;
- static_assert(!std::is_default_constructible_v<JoinIterator>);
+ [[maybe_unused]] JoinIterator it;
}
constexpr bool test() {
- test_non_default_constructible<cpp17_input_iterator<ChildView*>>();
- // NOTE: cpp20_input_iterator can't be used with join_view because it is not copyable.
+ test_default_constructible<cpp17_input_iterator<ChildView*>>();
+ test_default_constructible<cpp20_input_iterator<ChildView*>>();
test_default_constructible<forward_iterator<ChildView*>>();
test_default_constructible<bidirectional_iterator<ChildView*>>();
test_default_constructible<random_access_iterator<ChildView*>>();
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.iterator/ctor.other.pass.cpp
similarity index 97%
rename from libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/ctor.other.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.join/range.join.iterator/ctor.other.pass.cpp
index a0406f90c88c63..e220b2cfeac84a 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/ctor.other.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.iterator/ctor.other.pass.cpp
@@ -7,7 +7,6 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
-// UNSUPPORTED: !c++experimental
// constexpr iterator(iterator<!Const> i)
// requires Const &&
@@ -37,7 +36,7 @@ constexpr bool test() {
{
CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]),
CopyableChild(buffer[3])};
- std::ranges::join_view jv(CopyableParent{children});
+ std::ranges::join_view jv(ForwardCopyableParent{children});
auto iter1 = jv.begin();
using iterator = decltype(iter1);
using const_iterator = decltype(std::as_const(jv).begin());
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/decrement.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.iterator/decrement.pass.cpp
similarity index 90%
rename from libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/decrement.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.join/range.join.iterator/decrement.pass.cpp
index 4363fb0e330c3b..29720d93bab66f 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/decrement.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.iterator/decrement.pass.cpp
@@ -7,7 +7,6 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
-// UNSUPPORTED: !c++experimental
// constexpr iterator& operator--();
// requires ref-is-glvalue && bidirectional_range<Base> &&
@@ -18,9 +17,12 @@
// bidirectional_range<range_reference_t<Base>> &&
// common_range<range_reference_t<Base>>;
+#include <algorithm>
+#include <array>
#include <cassert>
#include <ranges>
#include <type_traits>
+#include <vector>
#include "../types.h"
@@ -150,6 +152,15 @@ constexpr bool test() {
static_assert(!CanPostDecrement<decltype(iter)>);
}
+ {
+ // LWG3791: `join_view::iterator::operator--` may be ill-formed
+ std::vector<std::vector<int>> vec = {{1, 2}, {3, 4}, {5, 6}};
+ auto r = vec | std::views::transform([](auto& x) -> auto&& { return std::move(x); }) | std::views::join;
+ auto e = --r.end();
+ assert(*e == 6);
+ assert(std::ranges::equal(std::views::reverse(r), std::array{6, 5, 4, 3, 2, 1}));
+ }
+
return true;
}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/eq.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.iterator/eq.pass.cpp
similarity index 89%
rename from libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/eq.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.join/range.join.iterator/eq.pass.cpp
index 327cc82b06b085..5c831f33e67c70 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/eq.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.iterator/eq.pass.cpp
@@ -7,10 +7,9 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
-// UNSUPPORTED: !c++experimental
// friend constexpr bool operator==(const iterator& x, const iterator& y);
-// requires ref-is-glvalue && equality_comparable<iterator_t<Base>> &&
+// requires ref-is-glvalue && forward_range<Base> &&
// equality_comparable<iterator_t<range_reference_t<Base>>>;
#include <cassert>
@@ -43,7 +42,7 @@ constexpr bool test() {
}
{
- // !equality_comparable<iterator_t<Base>>
+ // !forward_range<Base>
using Inner = BufferView<int*>;
using Outer = BufferView<cpp20_input_iterator<Inner*>, sentinel_wrapper<cpp20_input_iterator<Inner*>>>;
static_assert(!std::equality_comparable<std::ranges::iterator_t<Outer>>);
@@ -51,8 +50,6 @@ constexpr bool test() {
std::ranges::join_view jv(Outer{inners});
auto iter = jv.begin();
static_assert(!std::equality_comparable<decltype(iter)>);
- auto const_iter = std::as_const(jv).begin();
- static_assert(!std::equality_comparable<decltype(const_iter)>);
}
{
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.iterator/increment.pass.cpp
similarity index 94%
rename from libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/increment.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.join/range.join.iterator/increment.pass.cpp
index 4bcb4de7e9c886..dada91462a73ff 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/increment.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.iterator/increment.pass.cpp
@@ -7,7 +7,6 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
-// UNSUPPORTED: !c++experimental
// constexpr iterator& operator++();
// constexpr void operator++(int);
@@ -205,6 +204,23 @@ constexpr bool test() {
static_assert(std::is_void_v<decltype(iter++)>);
}
+ {
+ // Check stashing iterators (LWG3698: regex_iterator and join_view don't work together very well)
+ std::ranges::join_view<StashingRange> jv;
+ auto it = jv.begin();
+ assert(*it == 'a');
+ ++it;
+ assert(*it == 'a');
+ ++it;
+ assert(*it == 'b');
+ it++;
+ assert(*it == 'a');
+ it++;
+ assert(*it == 'b');
+ ++it;
+ assert(*it == 'c');
+ }
+
return true;
}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/iter.move.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.iterator/iter.move.pass.cpp
similarity index 98%
rename from libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/iter.move.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.join/range.join.iterator/iter.move.pass.cpp
index 0bf6aa3d926146..917e72dc858545 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/iter.move.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.iterator/iter.move.pass.cpp
@@ -7,7 +7,6 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
-// UNSUPPORTED: !c++experimental
// friend constexpr decltype(auto) iter_move(const iterator& i);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/iter.swap.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.iterator/iter.swap.pass.cpp
similarity index 98%
rename from libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/iter.swap.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.join/range.join.iterator/iter.swap.pass.cpp
index e9b73f1a415966..28e1bf75726f63 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/iter.swap.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.iterator/iter.swap.pass.cpp
@@ -7,7 +7,6 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
-// UNSUPPORTED: !c++experimental
// friend constexpr void iter_swap(const iterator& x, const iterator& y);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/member_types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.iterator/member_types.compile.pass.cpp
similarity index 99%
rename from libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/member_types.compile.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.join/range.join.iterator/member_types.compile.pass.cpp
index 17b98facd65081..b9b9d73d77e265 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/member_types.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.iterator/member_types.compile.pass.cpp
@@ -7,7 +7,6 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
-// UNSUPPORTED: !c++experimental
// Iterator traits and member typedefs in join_view::<iterator>.
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/star.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.iterator/star.pass.cpp
similarity index 97%
rename from libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/star.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.join/range.join.iterator/star.pass.cpp
index fa6f7bb031207a..73457b826df0b0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/star.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.iterator/star.pass.cpp
@@ -7,7 +7,6 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
-// UNSUPPORTED: !c++experimental
// constexpr decltype(auto) operator*() const;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/sentinel/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.default.pass.cpp
similarity index 95%
rename from libcxx/test/std/ranges/range.adaptors/range.join.view/sentinel/ctor.default.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.default.pass.cpp
index 0eebe14af3fcba..42fcc733e181f4 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.view/sentinel/ctor.default.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.default.pass.cpp
@@ -7,7 +7,6 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
-// UNSUPPORTED: !c++experimental
// sentinel() = default;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/sentinel/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.other.pass.cpp
similarity index 99%
rename from libcxx/test/std/ranges/range.adaptors/range.join.view/sentinel/ctor.other.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.other.pass.cpp
index 96196dcbaa5b31..5ef3e7416ef10b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.view/sentinel/ctor.other.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.other.pass.cpp
@@ -7,7 +7,6 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
-// UNSUPPORTED: !c++experimental
// constexpr sentinel(sentinel<!Const> s);
// requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp
similarity index 97%
rename from libcxx/test/std/ranges/range.adaptors/range.join.view/sentinel/ctor.parent.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp
index a9df7c3881ba8f..1ac68277338fee 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.view/sentinel/ctor.parent.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/ctor.parent.pass.cpp
@@ -7,7 +7,6 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
-// UNSUPPORTED: !c++experimental
// constexpr explicit sentinel(Parent& parent);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/sentinel/eq.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/eq.pass.cpp
similarity index 81%
rename from libcxx/test/std/ranges/range.adaptors/range.join.view/sentinel/eq.pass.cpp
rename to libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/eq.pass.cpp
index cbd03b84f208b9..bc7d4bec94d3e6 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.view/sentinel/eq.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.join/range.join.sentinel/eq.pass.cpp
@@ -7,7 +7,6 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
-// UNSUPPORTED: !c++experimental
// template<bool OtherConst>
// requires sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>>
@@ -17,6 +16,7 @@
#include <concepts>
#include <functional>
#include <ranges>
+#include <type_traits>
#include "../types.h"
@@ -61,18 +61,27 @@ static_assert(EqualityComparable<std::ranges::iterator_t<const ConstComparableVi
constexpr bool test() {
int buffer[4][4] = {{1111, 2222, 3333, 4444}, {555, 666, 777, 888}, {99, 1010, 1111, 1212}, {13, 14, 15, 16}};
+ // test iterator<false> == sentinel<false>
{
ChildView children[4] = {ChildView(buffer[0]), ChildView(buffer[1]), ChildView(buffer[2]), ChildView(buffer[3])};
- auto jv = std::ranges::join_view(ParentView(children));
+ auto jv = std::ranges::join_view(ParentView(children));
assert(jv.end() == std::ranges::next(jv.begin(), 16));
- static_assert(!EqualityComparable<decltype(std::as_const(jv).begin()), decltype(jv.end())>);
- static_assert(!EqualityComparable<decltype(jv.begin()), decltype(std::as_const(jv).end())>);
}
+ // test iterator<false> == sentinel<true>
+ {
+ ChildView children[4] = {ChildView(buffer[0]), ChildView(buffer[1]), ChildView(buffer[2]), ChildView(buffer[3])};
+ using ParentT = std::remove_all_extents_t<decltype(children)>;
+ auto jv = std::ranges::join_view(ForwardParentView<ParentT>(children));
+ assert(std::as_const(jv).end() == std::ranges::next(jv.begin(), 16));
+ }
+
+ // test iterator<true> == sentinel<true>
{
CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]),
CopyableChild(buffer[3])};
- const auto jv = std::ranges::join_view(ParentView(children));
+ using ParentT = std::remove_all_extents_t<decltype(children)>;
+ const auto jv = std::ranges::join_view(ForwardParentView<ParentT>(children));
assert(jv.end() == std::ranges::next(jv.begin(), 16));
}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.join/types.h
similarity index 88%
rename from libcxx/test/std/ranges/range.adaptors/range.join.view/types.h
rename to libcxx/test/std/ranges/range.adaptors/range.join/types.h
index b2ef5f090b5731..c1378dc1144b40 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.view/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.join/types.h
@@ -11,6 +11,7 @@
#include <concepts>
#include <cstdint>
+#include <string>
#include <tuple>
#include "test_macros.h"
@@ -52,13 +53,13 @@ inline ChildView globalChildren[4] = {
ChildView(globalBuffer[3]),
};
-template <class T>
+template <class T, template<class...> class Iter = cpp17_input_iterator>
struct ParentView : std::ranges::view_base {
T* ptr_;
unsigned size_;
- using iterator = cpp20_input_iterator<T*>;
- using const_iterator = cpp20_input_iterator<const T*>;
+ using iterator = Iter<T*>;
+ using const_iterator = Iter<const T*>;
using sentinel = sentinel_wrapper<iterator>;
using const_sentinel = sentinel_wrapper<const_iterator>;
@@ -80,6 +81,9 @@ struct ParentView : std::ranges::view_base {
template <class T>
ParentView(T*) -> ParentView<T>;
+template<class T>
+using ForwardParentView = ParentView<T, forward_iterator>;
+
struct CopyableChild : std::ranges::view_base {
int* ptr_;
unsigned size_;
@@ -97,15 +101,16 @@ struct CopyableChild : std::ranges::view_base {
constexpr const_sentinel end() const { return const_sentinel(const_iterator(ptr_ + size_)); }
};
-struct CopyableParent : std::ranges::view_base {
+template<template<class...> class Iter>
+struct CopyableParentTemplate : std::ranges::view_base {
CopyableChild* ptr_;
- using iterator = cpp17_input_iterator<CopyableChild*>;
- using const_iterator = cpp17_input_iterator<const CopyableChild*>;
+ using iterator = Iter<CopyableChild*>;
+ using const_iterator = Iter<const CopyableChild*>;
using sentinel = sentinel_wrapper<iterator>;
using const_sentinel = sentinel_wrapper<const_iterator>;
- constexpr CopyableParent(CopyableChild* ptr) : ptr_(ptr) {}
+ constexpr CopyableParentTemplate(CopyableChild* ptr) : ptr_(ptr) {}
constexpr iterator begin() { return iterator(ptr_); }
constexpr const_iterator begin() const { return const_iterator(ptr_); }
@@ -113,6 +118,9 @@ struct CopyableParent : std::ranges::view_base {
constexpr const_sentinel end() const { return const_sentinel(const_iterator(ptr_ + 4)); }
};
+using CopyableParent = CopyableParentTemplate<cpp17_input_iterator>;
+using ForwardCopyableParent = CopyableParentTemplate<forward_iterator>;
+
struct Box {
int x;
};
@@ -392,4 +400,48 @@ struct IterMoveSwapAwareView : BufferView<int*> {
};
static_assert(std::ranges::input_range<IterMoveSwapAwareView>);
+class StashingIterator {
+public:
+ using
diff erence_type = std::ptr
diff _t;
+ using value_type = std::string;
+
+ constexpr StashingIterator() : letter_('a') {}
+
+ constexpr StashingIterator& operator++() {
+ str_ += letter_;
+ ++letter_;
+ return *this;
+ }
+
+ constexpr void operator++(int) { ++*this; }
+
+ constexpr value_type operator*() const { return str_; }
+
+ constexpr bool operator==(std::default_sentinel_t) const { return letter_ > 'z'; }
+
+private:
+ char letter_;
+ value_type str_;
+};
+
+using StashingRange = std::ranges::subrange<StashingIterator, std::default_sentinel_t>;
+static_assert(std::ranges::input_range<StashingRange>);
+static_assert(!std::ranges::forward_range<StashingRange>);
+
+class ConstNonJoinableRange : public std::ranges::view_base {
+public:
+ constexpr StashingIterator begin() { return {}; }
+ constexpr std::default_sentinel_t end() { return {}; }
+
+ constexpr const int* begin() const { return &val_; }
+ constexpr const int* end() const { return &val_ + 1; }
+
+private:
+ int val_ = 1;
+};
+static_assert(std::ranges::input_range<ConstNonJoinableRange>);
+static_assert(std::ranges::input_range<const ConstNonJoinableRange>);
+static_assert(std::ranges::input_range<std::ranges::range_reference_t<ConstNonJoinableRange>>);
+static_assert(!std::ranges::input_range<std::ranges::range_reference_t<const ConstNonJoinableRange>>);
+
#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_JOIN_TYPES_H
diff --git a/libcxx/test/std/re/re.iter/re.regiter/iterator_concept_conformance.compile.pass.cpp b/libcxx/test/std/re/re.iter/re.regiter/iterator_concept_conformance.compile.pass.cpp
index 6f2da091c3709b..ad61baa76018da 100644
--- a/libcxx/test/std/re/re.iter/re.regiter/iterator_concept_conformance.compile.pass.cpp
+++ b/libcxx/test/std/re/re.iter/re.regiter/iterator_concept_conformance.compile.pass.cpp
@@ -14,8 +14,8 @@
#include <iterator>
-static_assert(std::forward_iterator<std::cregex_iterator>);
-static_assert(!std::bidirectional_iterator<std::cregex_iterator>);
+static_assert(std::input_iterator<std::cregex_iterator>);
+static_assert(!std::forward_iterator<std::cregex_iterator>);
static_assert(!std::indirectly_writable<std::cregex_iterator, char>);
static_assert(std::sentinel_for<std::cregex_iterator, std::cregex_iterator>);
static_assert(!std::sized_sentinel_for<std::cregex_iterator, std::cregex_iterator>);
diff --git a/libcxx/test/std/re/re.iter/re.regiter/types.pass.cpp b/libcxx/test/std/re/re.iter/re.regiter/types.pass.cpp
index 7d30b0adcc234c..8ee2c5006d31c4 100644
--- a/libcxx/test/std/re/re.iter/re.regiter/types.pass.cpp
+++ b/libcxx/test/std/re/re.iter/re.regiter/types.pass.cpp
@@ -20,6 +20,7 @@
// typedef const value_type* pointer;
// typedef const value_type& reference;
// typedef forward_iterator_tag iterator_category;
+// typedef input_iterator_tag iterator_concept; // since C++20
#include <regex>
#include <type_traits>
@@ -36,6 +37,9 @@ test()
static_assert((std::is_same<typename I::pointer, const std::match_results<const CharT*>*>::value), "");
static_assert((std::is_same<typename I::reference, const std::match_results<const CharT*>&>::value), "");
static_assert((std::is_same<typename I::iterator_category, std::forward_iterator_tag>::value), "");
+#if TEST_STD_VER >= 20
+ static_assert(std::is_same_v<typename I::iterator_concept, std::input_iterator_tag>);
+#endif
}
int main(int, char**)
diff --git a/libcxx/test/std/re/re.iter/re.tokiter/iterator_concept_conformance.compile.pass.cpp b/libcxx/test/std/re/re.iter/re.tokiter/iterator_concept_conformance.compile.pass.cpp
index 397226552edee0..23eea7f369c170 100644
--- a/libcxx/test/std/re/re.iter/re.tokiter/iterator_concept_conformance.compile.pass.cpp
+++ b/libcxx/test/std/re/re.iter/re.tokiter/iterator_concept_conformance.compile.pass.cpp
@@ -14,8 +14,8 @@
#include <iterator>
-static_assert(std::forward_iterator<std::cregex_token_iterator>);
-static_assert(!std::bidirectional_iterator<std::cregex_token_iterator>);
+static_assert(std::input_iterator<std::cregex_token_iterator>);
+static_assert(!std::forward_iterator<std::cregex_token_iterator>);
static_assert(!std::indirectly_writable<std::cregex_token_iterator, char>);
static_assert(std::sentinel_for<std::cregex_token_iterator, std::cregex_token_iterator>);
static_assert(!std::sized_sentinel_for<std::cregex_token_iterator, std::cregex_token_iterator>);
diff --git a/libcxx/test/std/re/re.iter/re.tokiter/types.pass.cpp b/libcxx/test/std/re/re.iter/re.tokiter/types.pass.cpp
index 73ad58f4eecfb6..a9c18e8a1b77a1 100644
--- a/libcxx/test/std/re/re.iter/re.tokiter/types.pass.cpp
+++ b/libcxx/test/std/re/re.iter/re.tokiter/types.pass.cpp
@@ -20,6 +20,7 @@
// typedef const value_type* pointer;
// typedef const value_type& reference;
// typedef forward_iterator_tag iterator_category;
+// typedef input_iterator_tag iterator_concept; // since C++20
#include <regex>
#include <type_traits>
@@ -36,6 +37,9 @@ test()
static_assert((std::is_same<typename I::pointer, const std::sub_match<const CharT*>*>::value), "");
static_assert((std::is_same<typename I::reference, const std::sub_match<const CharT*>&>::value), "");
static_assert((std::is_same<typename I::iterator_category, std::forward_iterator_tag>::value), "");
+#if TEST_STD_VER >= 20
+ static_assert(std::is_same_v<typename I::iterator_concept, std::input_iterator_tag>);
+#endif
}
int main(int, char**)
More information about the libcxx-commits
mailing list