diff --git a/stl/inc/atomic b/stl/inc/atomic index babd2bf3d49..6848b13c053 100644 --- a/stl/inc/atomic +++ b/stl/inc/atomic @@ -107,6 +107,10 @@ extern "C" inline void _Check_memory_order(const unsigned int _Order) noexcept { } } +// note: these macros are _not_ always safe to use with a trailing semicolon, +// we avoid wrapping them in do {} while (0) because MSVC generates code for such loops +// in debug mode. + #if defined(_M_IX86) || (defined(_M_X64) && !defined(_M_ARM64EC)) #define _ATOMIC_CHOOSE_INTRINSIC(_Order, _Result, _Intrinsic, ...) \ _Check_memory_order(_Order); \ @@ -160,23 +164,20 @@ extern "C" inline void _Check_memory_order(const unsigned int _Order) noexcept { #endif // _STD_ATOMIC_USE_ARM64_LDAR_STLR == 1 -// note: these macros are _not_ always safe to use with a trailing semicolon, -// we avoid wrapping them in do {} while (0) because MSVC generates code for such loops -// in debug mode. -#define _ATOMIC_LOAD_VERIFY_MEMORY_ORDER(_Order_var) \ - switch (_Order_var) { \ - case _Atomic_memory_order_relaxed: \ - break; \ - case _Atomic_memory_order_consume: \ - case _Atomic_memory_order_acquire: \ - case _Atomic_memory_order_seq_cst: \ - _Compiler_or_memory_barrier(); \ - break; \ - case _Atomic_memory_order_release: \ - case _Atomic_memory_order_acq_rel: \ - default: \ - _INVALID_MEMORY_ORDER; \ - break; \ +#define _ATOMIC_POST_LOAD_BARRIER_AS_NEEDED(_Order_var) \ + switch (_Order_var) { \ + case _Atomic_memory_order_relaxed: \ + break; \ + case _Atomic_memory_order_consume: \ + case _Atomic_memory_order_acquire: \ + case _Atomic_memory_order_seq_cst: \ + _Compiler_or_memory_barrier(); \ + break; \ + case _Atomic_memory_order_release: \ + case _Atomic_memory_order_acq_rel: \ + default: \ + _INVALID_MEMORY_ORDER; \ + break; \ } #if _STD_ATOMIC_USE_ARM64_LDAR_STLR == 1 @@ -185,13 +186,13 @@ extern "C" inline void _Check_memory_order(const unsigned int _Order) noexcept { _Compiler_barrier(); \ __stlr##_Width(reinterpret_cast(_Ptr), (_Desired)); -#else +#else // ^^^ _STD_ATOMIC_USE_ARM64_LDAR_STLR == 1 ^^^ / vvv _STD_ATOMIC_USE_ARM64_LDAR_STLR == 0 vvv #define __STORE_RELEASE(_Width, _Ptr, _Desired) \ _Compiler_or_memory_barrier(); \ __iso_volatile_store##_Width((_Ptr), (_Desired)); -#endif +#endif // ^^^ _STD_ATOMIC_USE_ARM64_LDAR_STLR == 0 ^^^ #define _ATOMIC_STORE_PREFIX(_Width, _Ptr, _Desired) \ case _Atomic_memory_order_relaxed: \ @@ -207,7 +208,6 @@ extern "C" inline void _Check_memory_order(const unsigned int _Order) noexcept { _INVALID_MEMORY_ORDER; \ _FALLTHROUGH; - #define _ATOMIC_STORE_SEQ_CST_ARM(_Width, _Ptr, _Desired) \ _Memory_barrier(); \ __iso_volatile_store##_Width((_Ptr), (_Desired)); \ @@ -788,7 +788,7 @@ struct _Atomic_storage<_Ty, 1> { // lock-free using 1-byte intrinsics _ATOMIC_LOAD_ARM64(_As_bytes, 8, _Mem, static_cast(_Order)) #else _As_bytes = __iso_volatile_load8(_Mem); - _ATOMIC_LOAD_VERIFY_MEMORY_ORDER(static_cast(_Order)) + _ATOMIC_POST_LOAD_BARRIER_AS_NEEDED(static_cast(_Order)) #endif return reinterpret_cast<_TVal&>(_As_bytes); } @@ -896,7 +896,7 @@ struct _Atomic_storage<_Ty, 2> { // lock-free using 2-byte intrinsics _ATOMIC_LOAD_ARM64(_As_bytes, 16, _Mem, static_cast(_Order)) #else _As_bytes = __iso_volatile_load16(_Mem); - _ATOMIC_LOAD_VERIFY_MEMORY_ORDER(static_cast(_Order)) + _ATOMIC_POST_LOAD_BARRIER_AS_NEEDED(static_cast(_Order)) #endif return reinterpret_cast<_TVal&>(_As_bytes); } @@ -1003,7 +1003,7 @@ struct _Atomic_storage<_Ty, 4> { // lock-free using 4-byte intrinsics _ATOMIC_LOAD_ARM64(_As_bytes, 32, _Mem, static_cast(_Order)) #else _As_bytes = __iso_volatile_load32(_Mem); - _ATOMIC_LOAD_VERIFY_MEMORY_ORDER(static_cast(_Order)) + _ATOMIC_POST_LOAD_BARRIER_AS_NEEDED(static_cast(_Order)) #endif return reinterpret_cast<_TVal&>(_As_bytes); } @@ -1120,7 +1120,7 @@ struct _Atomic_storage<_Ty, 8> { // lock-free using 8-byte intrinsics _As_bytes = __iso_volatile_load64(_Mem); #endif - _ATOMIC_LOAD_VERIFY_MEMORY_ORDER(static_cast(_Order)) + _ATOMIC_POST_LOAD_BARRIER_AS_NEEDED(static_cast(_Order)) #endif // _STD_ATOMIC_USE_ARM64_LDAR_STLR == 1 return reinterpret_cast<_TVal&>(_As_bytes); } @@ -3045,7 +3045,7 @@ _STD_END #undef _CMPXCHG_MASK_OUT_PADDING_BITS #undef _ATOMIC_CHOOSE_INTRINSIC -#undef _ATOMIC_LOAD_VERIFY_MEMORY_ORDER +#undef _ATOMIC_POST_LOAD_BARRIER_AS_NEEDED #undef _ATOMIC_STORE_PREFIX #undef _ATOMIC_STORE_SEQ_CST_ARM #undef _ATOMIC_STORE_SEQ_CST_X86_X64 diff --git a/stl/inc/concepts b/stl/inc/concepts index 35845c6c674..15845cb6d71 100644 --- a/stl/inc/concepts +++ b/stl/inc/concepts @@ -39,16 +39,6 @@ _EXPORT_STD template concept derived_from = __is_base_of(_Base, _Derived) && __is_convertible_to(const volatile _Derived*, const volatile _Base*); -_EXPORT_STD template -#if !defined(__EDG__) && !defined(__clang__) // TRANSITION, DevCom-1627396 -concept convertible_to = is_convertible_v<_From, _To> -#else // ^^^ workaround / no workaround vvv -concept convertible_to = __is_convertible_to(_From, _To) -#endif // ^^^ no workaround ^^^ - && requires { - static_cast<_To>(_STD declval<_From>()); - }; - template concept _Implicitly_convertible_to = is_convertible_v<_From, _To>; diff --git a/stl/inc/expected b/stl/inc/expected index 9d4a583cfd0..573d1e7d69b 100644 --- a/stl/inc/expected +++ b/stl/inc/expected @@ -732,7 +732,7 @@ public: // [expected.object.monadic] template - requires is_copy_constructible_v<_Err> + requires is_constructible_v<_Err, _Err&> constexpr auto and_then(_Fn&& _Func) & { using _Uty = remove_cvref_t>; @@ -789,7 +789,7 @@ public: } template - requires is_move_constructible_v<_Err> + requires is_constructible_v<_Err, const _Err> constexpr auto and_then(_Fn&& _Func) const&& { using _Uty = remove_cvref_t>; @@ -808,7 +808,7 @@ public: } template - requires is_copy_constructible_v<_Ty> + requires is_constructible_v<_Ty, _Ty&> constexpr auto or_else(_Fn&& _Func) & { using _Uty = remove_cvref_t>; @@ -865,7 +865,7 @@ public: } template - requires is_move_constructible_v<_Ty> + requires is_constructible_v<_Ty, const _Ty> constexpr auto or_else(_Fn&& _Func) const&& { using _Uty = remove_cvref_t>; @@ -884,7 +884,7 @@ public: } template - requires is_copy_constructible_v<_Err> + requires is_constructible_v<_Err, _Err&> constexpr auto transform(_Fn&& _Func) & { static_assert(invocable<_Fn, _Ty&>, "expected::transform(F) requires that F is invocable with T. " "(N4928 [expected.object.monadic]/19)"); @@ -965,7 +965,7 @@ public: } template - requires is_move_constructible_v<_Err> + requires is_constructible_v<_Err, const _Err> constexpr auto transform(_Fn&& _Func) const&& { static_assert(invocable<_Fn, const _Ty>, "expected::transform(F) requires that F is invocable with T. " "(N4928 [expected.object.monadic]/23)"); @@ -992,7 +992,7 @@ public: } template - requires is_copy_constructible_v<_Ty> + requires is_constructible_v<_Ty, _Ty&> constexpr auto transform_error(_Fn&& _Func) & { static_assert(invocable<_Fn, _Err&>, "expected::transform_error(F) requires that F is invocable with E. " "(N4928 [expected.object.monadic]/27)"); @@ -1053,7 +1053,7 @@ public: } template - requires is_move_constructible_v<_Ty> + requires is_constructible_v<_Ty, const _Ty> constexpr auto transform_error(_Fn&& _Func) const&& { static_assert(invocable<_Fn, const _Err>, "expected::transform_error(F) requires that F is invocable with E. " @@ -1470,7 +1470,7 @@ public: // [expected.void.monadic] template - requires is_copy_constructible_v<_Err> + requires is_constructible_v<_Err, _Err&> constexpr auto and_then(_Fn&& _Func) & { using _Uty = remove_cvref_t>; @@ -1527,7 +1527,7 @@ public: } template - requires is_move_constructible_v<_Err> + requires is_constructible_v<_Err, const _Err> constexpr auto and_then(_Fn&& _Func) const&& { using _Uty = remove_cvref_t>; @@ -1618,7 +1618,7 @@ public: } template - requires is_copy_constructible_v<_Err> + requires is_constructible_v<_Err, _Err&> constexpr auto transform(_Fn&& _Func) & { static_assert(invocable<_Fn>, "expected::transform(F) requires that F is invocable with no arguments. " "(N4928 [expected.void.monadic]/17)"); @@ -1696,7 +1696,7 @@ public: } template - requires is_move_constructible_v<_Err> + requires is_constructible_v<_Err, const _Err> constexpr auto transform(_Fn&& _Func) const&& { static_assert(invocable<_Fn>, "expected::transform(F) requires that F is invocable with no arguments. " "(N4928 [expected.void.monadic]/21)"); diff --git a/stl/inc/format b/stl/inc/format index 7c78aada0bd..aa4066bc5e0 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -1410,13 +1410,17 @@ _NODISCARD constexpr basic_format_arg<_Context> _Get_arg(const _Context& _Ctx, c return _Arg; } +template +inline constexpr bool _Is_signed_or_unsigned_large_integer_t = + _Is_any_of_v, int, unsigned int, long, unsigned long, long long, unsigned long long>; + // Checks that the type and value of an argument associated with a dynamic // width specifier are valid. class _Width_checker { public: template _NODISCARD constexpr unsigned long long operator()(const _Ty _Value) const { - if constexpr (is_integral_v<_Ty>) { + if constexpr (_Is_signed_or_unsigned_large_integer_t<_Ty>) { if constexpr (is_signed_v<_Ty>) { if (_Value < 0) { _Throw_format_error("Negative width."); @@ -1435,7 +1439,7 @@ class _Precision_checker { public: template _NODISCARD constexpr unsigned long long operator()(const _Ty _Value) const { - if constexpr (is_integral_v<_Ty>) { + if constexpr (_Is_signed_or_unsigned_large_integer_t<_Ty>) { if constexpr (is_signed_v<_Ty>) { if (_Value < 0) { _Throw_format_error("Negative precision."); diff --git a/stl/inc/memory b/stl/inc/memory index 3ddb1c45401..e7ca9c2cfc2 100644 --- a/stl/inc/memory +++ b/stl/inc/memory @@ -4179,7 +4179,15 @@ public: explicit out_ptr_t(_SmartPtr& _Smart_ptr_, _ArgsT... _Args_) noexcept( is_nothrow_constructible_v, _ArgsT...>) /* strengthened */ : _Smart_ptr(_Smart_ptr_), - _Mypair(_One_then_variadic_args_t{}, tuple<_ArgsT...>{_STD forward<_ArgsT>(_Args_)...}) {} + _Mypair(_One_then_variadic_args_t{}, tuple<_ArgsT...>{_STD forward<_ArgsT>(_Args_)...}) { + constexpr bool _Is_resettable = requires { _Smart_ptr.reset(); }; // TRANSITION, DevCom-10291456 + if constexpr (_Is_resettable) { + _Smart_ptr.reset(); + } else { + static_assert(is_constructible_v<_SmartPtr>, "the adapted pointer type must be default constructible."); + _Smart_ptr = _SmartPtr(); + } + } out_ptr_t(const out_ptr_t&) = delete; diff --git a/stl/inc/memory_resource b/stl/inc/memory_resource index 62b0f428dbf..16d8d0c9756 100644 --- a/stl/inc/memory_resource +++ b/stl/inc/memory_resource @@ -513,6 +513,10 @@ namespace pmr { void _Increase_capacity(unsynchronized_pool_resource& _Pool_resource) { // this pool has no free blocks; get a new chunk from upstream + if (_Next_capacity > _Pool_resource._Options.max_blocks_per_chunk) { + // This is a fresh pool, _Next_capacity hasn't yet been bounded by max_blocks_per_chunk: + _Next_capacity = _Pool_resource._Options.max_blocks_per_chunk; + } const size_t _Size = _Size_for_capacity(_Next_capacity); memory_resource* const _Resource = _Pool_resource.upstream_resource(); void* const _Ptr = _Resource->allocate(_Size, _Block_size); diff --git a/stl/inc/ranges b/stl/inc/ranges index f99d906a71f..66e94c498ad 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -19,6 +19,10 @@ _EMIT_STL_WARNING(STL4038, "The contents of are available only with C++ #include #include +#if _HAS_CXX23 +#include +#endif // _HAS_CXX23 + #pragma pack(push, _CRT_PACKING) #pragma warning(push, _STL_WARNING_LEVEL) #pragma warning(disable : _STL_DISABLED_WARNINGS) @@ -3567,37 +3571,36 @@ namespace ranges { #ifdef __clang__ template // TRANSITION, LLVM-47414 - concept _Can_const_join = input_range && is_reference_v>; + concept _Can_const_join = forward_range && is_reference_v> + && input_range>; #endif // ^^^ workaround ^^^ + template + _NODISCARD constexpr _Ty& _As_lvalue(_Ty&& _Val) noexcept { + return static_cast<_Ty&>(_Val); + } + template requires is_reference_v> class _Join_view_base<_Vw> : public view_interface> {}; + template + class _Join_view_outer_iter_base : public _Join_view_base<_Vw> { + protected: + _Non_propagating_cache> _Outer; + }; + + template + class _Join_view_outer_iter_base<_Vw> : public _Join_view_base<_Vw> {}; + _EXPORT_STD template requires view<_Vw> && input_range> - class join_view : public _Join_view_base<_Vw> { - -#ifndef _USE_JOIN_VIEW_INPUT_RANGE - static_assert(forward_range<_Vw>, - "Due to a design flaw, join_view can misbehave " - "with some input-only ranges (see https://wg21.link/lwg3698). " - "We believe that WG21 will be unable to fix this problem without breaking ABI. " - "To minimize breakage when a fix is implemented, " - "we are temporarily disabling potentially problematic cases. " - "You can define _USE_JOIN_VIEW_INPUT_RANGE to suppress this error, " - "but be aware that you will almost certainly need to recompile " - "when we release a fix."); -#endif // _USE_JOIN_VIEW_INPUT_RANGE - + class join_view : public _Join_view_outer_iter_base<_Vw> { private: template using _InnerRng = range_reference_t<_Maybe_const<_Const, _Vw>>; /* [[no_unique_address]] */ _Vw _Range{}; - template - class _Sentinel; - template struct _Category_base {}; @@ -3614,14 +3617,28 @@ namespace ranges { }; template - class _Iterator + class _Iterator_base + : public _Category_base<_Maybe_const<_Const, _Vw>, _InnerRng<_Const>, is_reference_v<_InnerRng<_Const>>> {}; + + template + requires forward_range<_Maybe_const<_Const, _Vw>> + class _Iterator_base<_Const> : public _Category_base<_Maybe_const<_Const, _Vw>, _InnerRng<_Const>, is_reference_v<_InnerRng<_Const>>> { + protected: + using _OuterIter = iterator_t<_Maybe_const<_Const, _Vw>>; + + _Iterator_base() = default; + constexpr explicit _Iterator_base(_OuterIter&& _Outer_) : _Outer(_STD move(_Outer_)) {} + + /* [[no_unique_address]] */ _OuterIter _Outer{}; + }; + + template + class _Iterator : public _Iterator_base<_Const> { private: - template - friend class _Iterator; - template - friend class _Sentinel; + friend join_view; + using _Mybase = _Iterator_base<_Const>; using _Parent_t = _Maybe_const<_Const, join_view>; using _Base = _Maybe_const<_Const, _Vw>; using _OuterIter = iterator_t<_Base>; @@ -3630,21 +3647,53 @@ namespace ranges { // True if and only if the expression *i, where i is an iterator from the outer range, is a glvalue: static constexpr bool _Deref_is_glvalue = is_reference_v<_InnerRng<_Const>>; - /* [[no_unique_address]] */ _OuterIter _Outer{}; /* [[no_unique_address]] */ _Defaultabox<_InnerIter> _Inner{}; // Non-standard extension: when _Inner_iter // is default-constructible, we don't wrap in // an optional-like. _Parent_t* _Parent{}; + constexpr _Iterator(_Parent_t& _Parent_, _OuterIter _Outer_) + requires forward_range<_Base> + : _Mybase{_STD move(_Outer_)}, _Parent{_STD addressof(_Parent_)} { +#if _ITERATOR_DEBUG_LEVEL != 0 + _Adl_verify_range(this->_Outer, _RANGES end(_Parent_._Range)); + _Adl_verify_range(_RANGES begin(_Parent_._Range), this->_Outer); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + _Satisfy(); + } + + constexpr explicit _Iterator(_Parent_t& _Parent) + requires (!forward_range<_Base>) + : _Parent{_STD addressof(_Parent)} { + _Satisfy(); + } + + _NODISCARD constexpr _OuterIter& _Get_outer() noexcept { + if constexpr (forward_range<_Base>) { + return this->_Outer; + } else { + return *_Parent->_Outer; + } + } + + _NODISCARD constexpr const _OuterIter& _Get_outer() const noexcept { + if constexpr (forward_range<_Base>) { + return this->_Outer; + } else { + return *_Parent->_Outer; + } + } + constexpr auto&& _Update_inner() { if constexpr (_Deref_is_glvalue) { - return *_Outer; + return *_Get_outer(); } else { - return _Parent->_Inner._Emplace(_Not_quite_object::_Construct_tag{}, _Outer)._Val; + return _Parent->_Inner._Emplace(_Not_quite_object::_Construct_tag{}, _Get_outer())._Val; } } constexpr void _Satisfy() { + auto& _Outer = _Get_outer(); const auto _Last = _RANGES end(_Parent->_Range); for (; _Outer != _Last; ++_Outer) { auto&& _Tmp = _Update_inner(); @@ -3661,10 +3710,10 @@ namespace ranges { #if _ITERATOR_DEBUG_LEVEL != 0 constexpr void _Check_dereference() const noexcept { _STL_VERIFY(_Parent != nullptr, "cannot dereference value-initialized join_view iterator"); - _STL_VERIFY(_Outer != _RANGES end(_Parent->_Range), "cannot dereference join_view end iterator"); + _STL_VERIFY(_Get_outer() != _RANGES end(_Parent->_Range), "cannot dereference join_view end iterator"); sentinel_t<_InnerRng<_Const>> _Last; if constexpr (_Deref_is_glvalue) { - _Last = _RANGES end(*_Outer); + _Last = _RANGES end(_As_lvalue(*_Get_outer())); } else { _Last = _RANGES end((*_Parent->_Inner)._Val); } @@ -3691,21 +3740,10 @@ namespace ranges { _Iterator() requires default_initializable<_OuterIter> = default; // clang-format on - constexpr _Iterator(_Parent_t& _Parent_, _OuterIter _Outer_) - : _Outer{_STD move(_Outer_)}, _Parent{_STD addressof(_Parent_)} { -#if _ITERATOR_DEBUG_LEVEL != 0 - _Adl_verify_range(_Outer, _RANGES end(_Parent_._Range)); - if constexpr (forward_range<_Base>) { - _Adl_verify_range(_RANGES begin(_Parent_._Range), _Outer); - } -#endif // _ITERATOR_DEBUG_LEVEL != 0 - _Satisfy(); - } - constexpr _Iterator(_Iterator _It) requires _Const && convertible_to, _OuterIter> && convertible_to>, _InnerIter> - : _Outer(_STD move(_It._Outer)), _Inner(_STD move(_It._Inner)), _Parent(_It._Parent) {} + : _Mybase(_STD move(_It._Outer)), _Inner(_STD move(_It._Inner)), _Parent(_It._Parent) {} _NODISCARD constexpr decltype(auto) operator*() const noexcept(noexcept(**_Inner)) /* strengthened */ { #if _ITERATOR_DEBUG_LEVEL != 0 @@ -3725,8 +3763,9 @@ namespace ranges { } constexpr _Iterator& operator++() { + auto& _Outer = _Get_outer(); if constexpr (_Deref_is_glvalue) { - if (++*_Inner == _RANGES end(*_Outer)) { + if (++*_Inner == _RANGES end(_As_lvalue(*_Outer))) { ++_Outer; _Satisfy(); } @@ -3754,13 +3793,14 @@ namespace ranges { requires _Deref_is_glvalue && bidirectional_range<_Base> && bidirectional_range<_InnerRng<_Const>> && common_range<_InnerRng<_Const>> { // clang-format on + auto& _Outer = _Get_outer(); if (_Outer == _RANGES end(_Parent->_Range)) { --_Outer; - _Inner = _RANGES end(*_Outer); + _Inner = _RANGES end(_As_lvalue(*_Outer)); } - while (*_Inner == _RANGES begin(*_Outer)) { + while (*_Inner == _RANGES begin(_As_lvalue(*_Outer))) { --_Outer; - *_Inner = _RANGES end(*_Outer); + *_Inner = _RANGES end(_As_lvalue(*_Outer)); } --*_Inner; return *this; @@ -3780,7 +3820,7 @@ namespace ranges { _NODISCARD_FRIEND constexpr bool operator==(const _Iterator& _Left, const _Iterator& _Right) noexcept( noexcept(_Fake_copy_init(_Left._Outer == _Right._Outer && _Left._Inner == _Right._Inner))) /* strengthened */ - requires _Deref_is_glvalue && equality_comparable<_OuterIter> && equality_comparable<_InnerIter> { + requires _Deref_is_glvalue && forward_range<_Base> && equality_comparable<_InnerIter> { // clang-format on #if _ITERATOR_DEBUG_LEVEL != 0 _Left._Same_range(_Right); @@ -3812,10 +3852,7 @@ namespace ranges { template class _Sentinel { private: - template - friend class _Iterator; - template - friend class _Sentinel; + friend join_view; using _Parent_t = _Maybe_const<_Const, join_view>; using _Base = _Maybe_const<_Const, _Vw>; @@ -3829,9 +3866,9 @@ namespace ranges { template requires sentinel_for, _Maybe_const_iter<_OtherConst>> _NODISCARD constexpr bool _Equal(const _Iterator<_OtherConst>& _It) const noexcept( - noexcept(_Fake_copy_init(_It._Outer == _Last))) { + noexcept(_Fake_copy_init(_It._Get_outer() == _Last))) { // clang-format on - return _It._Outer == _Last; + return _It._Get_outer() == _Last; } public: @@ -3874,15 +3911,20 @@ namespace ranges { } _NODISCARD constexpr auto begin() { - constexpr bool _Use_const = _Simple_view<_Vw> && is_reference_v<_InnerRng>; - return _Iterator<_Use_const>{*this, _RANGES begin(_Range)}; + if constexpr (forward_range<_Vw>) { + constexpr bool _Use_const = _Simple_view<_Vw> && is_reference_v<_InnerRng>; + return _Iterator<_Use_const>{*this, _RANGES begin(_Range)}; + } else { + this->_Outer._Emplace(_RANGES begin(_Range)); + return _Iterator{*this}; + } } _NODISCARD constexpr _Iterator begin() const #ifdef __clang__ // TRANSITION, LLVM-47414 requires _Can_const_join<_Vw> #else // ^^^ workaround / no workaround vvv - requires input_range && is_reference_v<_InnerRng> + requires forward_range && is_reference_v<_InnerRng> && input_range<_InnerRng> #endif // TRANSITION, LLVM-47414 { return _Iterator{*this, _RANGES begin(_Range)}; @@ -3901,11 +3943,10 @@ namespace ranges { #ifdef __clang__ // TRANSITION, LLVM-47414 requires _Can_const_join<_Vw> #else // ^^^ workaround / no workaround vvv - requires input_range && is_reference_v<_InnerRng> + requires forward_range && is_reference_v<_InnerRng> && input_range<_InnerRng> #endif // TRANSITION, LLVM-47414 { - if constexpr (forward_range && forward_range<_InnerRng> && common_range - && common_range<_InnerRng>) { + if constexpr (forward_range<_InnerRng> && common_range && common_range<_InnerRng>) { return _Iterator{*this, _RANGES end(_Range)}; } else { return _Sentinel{*this}; @@ -3943,7 +3984,8 @@ namespace ranges { #ifdef __clang__ template // TRANSITION, LLVM-47414 concept _Can_const_join_with = - input_range && forward_range && is_reference_v>; + forward_range && forward_range && is_reference_v> + && input_range>; #endif // ^^^ workaround ^^^ _EXPORT_STD template @@ -3971,10 +4013,19 @@ namespace ranges { requires is_reference_v> class _Join_with_view_base<_Vw, _Pat> : public view_interface> {}; + template + class _Join_with_view_outer_iter_base : public _Join_with_view_base<_Vw, _Pat> { + protected: + _Non_propagating_cache> _Outer_it; + }; + + template + class _Join_with_view_outer_iter_base<_Vw, _Pat> : public _Join_with_view_base<_Vw, _Pat> {}; + _EXPORT_STD template requires view<_Vw> && input_range> && view<_Pat> && _Compatible_joinable_ranges, _Pat> - class join_with_view : public _Join_with_view_base<_Vw, _Pat> { + class join_with_view : public _Join_with_view_outer_iter_base<_Vw, _Pat> { private: template using _InnerRng = range_reference_t<_Maybe_const<_Const, _Vw>>; @@ -3982,9 +4033,6 @@ namespace ranges { /* [[no_unique_address]] */ _Vw _Range{}; /* [[no_unique_address]] */ _Pat _Pattern{}; - template - class _Sentinel; - template struct _Category_base {}; @@ -4011,10 +4059,26 @@ namespace ranges { }; template - class _Iterator : public _Category_base<_Const> { + class _Iterator_base : public _Category_base<_Const> {}; + + template + requires forward_range<_Maybe_const<_Const, _Vw>> + class _Iterator_base<_Const> : public _Category_base<_Const> { + protected: + using _OuterIter = iterator_t<_Maybe_const<_Const, _Vw>>; + + _Iterator_base() = default; + constexpr explicit _Iterator_base(_OuterIter&& _Outer_it_) : _Outer_it(_STD move(_Outer_it_)) {} + + /* [[no_unique_address]] */ _OuterIter _Outer_it{}; + }; + + template + class _Iterator : public _Iterator_base<_Const> { private: friend join_with_view; + using _Mybase = _Iterator_base<_Const>; using _Parent_t = _Maybe_const<_Const, join_with_view>; using _Base = _Maybe_const<_Const, _Vw>; using _PatternBase = _Maybe_const<_Const, _Pat>; @@ -4027,29 +4091,53 @@ namespace ranges { static constexpr bool _Deref_is_glvalue = is_reference_v<_InnerRng<_Const>>; _Parent_t* _Parent{}; - /* [[no_unique_address]] */ _OuterIter _Outer_it{}; _Variantish<_PatternIter, _InnerIter> _Inner_it{}; - constexpr _Iterator(_Parent_t& _Parent_, iterator_t<_Base> _Outer_) - : _Parent(_STD addressof(_Parent_)), _Outer_it(_STD move(_Outer_)) { - if (_Outer_it != _RANGES end(_Parent->_Range)) { - auto&& _Inner = _Update_inner(); - _Inner_it._Emplace_second(_RANGES begin(_Inner)); + constexpr _Iterator(_Parent_t& _Parent_, _OuterIter _Outer_) + requires forward_range<_Base> + : _Mybase(_STD move(_Outer_)), _Parent(_STD addressof(_Parent_)) { + if (this->_Outer_it != _RANGES end(_Parent->_Range)) { + _Inner_it._Emplace_second(_RANGES begin(_Update_inner())); + _Satisfy(); + } + } + + constexpr explicit _Iterator(_Parent_t& _Parent_) + requires (!forward_range<_Base>) + : _Parent{_STD addressof(_Parent_)} { + if (*_Parent->_Outer_it != _RANGES end(_Parent->_Range)) { + _Inner_it._Emplace_second(_RANGES begin(_Update_inner())); _Satisfy(); } } - _NODISCARD constexpr auto&& _Update_inner() { + _NODISCARD constexpr _OuterIter& _Get_outer() noexcept { + if constexpr (forward_range<_Base>) { + return this->_Outer_it; + } else { + return *_Parent->_Outer_it; + } + } + + _NODISCARD constexpr const _OuterIter& _Get_outer() const noexcept { + if constexpr (forward_range<_Base>) { + return this->_Outer_it; + } else { + return *_Parent->_Outer_it; + } + } + + _NODISCARD constexpr auto& _Update_inner() { if constexpr (_Deref_is_glvalue) { - return *_Outer_it; + return _As_lvalue(*_Get_outer()); } else { - return _Parent->_Inner._Emplace(_Not_quite_object::_Construct_tag{}, _Outer_it)._Val; + return _Parent->_Inner._Emplace(_Not_quite_object::_Construct_tag{}, _Get_outer())._Val; } } - _NODISCARD constexpr auto&& _Get_inner() noexcept { + _NODISCARD constexpr auto& _Get_inner() noexcept { if constexpr (_Deref_is_glvalue) { - return *_Outer_it; + return _As_lvalue(*_Get_outer()); } else { return (*_Parent->_Inner)._Val; } @@ -4073,16 +4161,15 @@ namespace ranges { break; } - auto&& _Inner = _Update_inner(); - _Inner_it._Emplace_second(_RANGES begin(_Inner)); + _Inner_it._Emplace_second(_RANGES begin(_Update_inner())); } else { _STL_INTERNAL_CHECK(_Inner_it._Contains == _Variantish_state::_Holds_second); - auto&& _Inner = _Get_inner(); - if (_Inner_it._Second != _RANGES end(_Inner)) { + if (_Inner_it._Second != _RANGES end(_Get_inner())) { break; } + auto& _Outer_it = _Get_outer(); ++_Outer_it; if (_Outer_it == _RANGES end(_Parent->_Range)) { if constexpr (_Deref_is_glvalue) { @@ -4115,7 +4202,7 @@ namespace ranges { && convertible_to, _OuterIter> // && convertible_to>, _InnerIter> // && convertible_to, _PatternIter> // - : _Parent(_It._Parent), _Outer_it(_STD move(_It._Outer_it)) { + : _Mybase(_STD move(_It._Outer_it)), _Parent(_It._Parent) { switch (_It._Inner_it._Contains) { case _Variantish_state::_Holds_first: _Inner_it._Emplace_first(_STD move(_It._Inner_it._First)); @@ -4164,10 +4251,10 @@ namespace ranges { requires _Deref_is_glvalue && bidirectional_range<_Base> // && _Bidi_common_range<_InnerRng<_Const>> && _Bidi_common_range<_PatternBase> { + auto& _Outer_it = _Get_outer(); if (_Outer_it == _RANGES end(_Parent->_Range)) { --_Outer_it; - auto&& _Inner = *_Outer_it; - _Inner_it._Emplace_second(_RANGES end(_Inner)); + _Inner_it._Emplace_second(_RANGES end(_Get_inner())); } for (;;) { @@ -4175,15 +4262,13 @@ namespace ranges { auto& _It = _Inner_it._First; if (_It == _RANGES begin(_Parent->_Pattern)) { --_Outer_it; - auto&& _Inner = *_Outer_it; - _Inner_it._Emplace_second(_RANGES end(_Inner)); + _Inner_it._Emplace_second(_RANGES end(_Get_inner())); } else { break; } } else if (_Inner_it._Contains == _Variantish_state::_Holds_second) { - auto& _It = _Inner_it._Second; - auto&& _Inner = *_Outer_it; - if (_It == _RANGES begin(_Inner)) { + auto& _It = _Inner_it._Second; + if (_It == _RANGES begin(_Get_inner())) { _Inner_it._Emplace_first(_RANGES end(_Parent->_Pattern)); } else { break; @@ -4216,7 +4301,7 @@ namespace ranges { } _NODISCARD_FRIEND constexpr bool operator==(const _Iterator& _Left, const _Iterator& _Right) - requires _Deref_is_glvalue && equality_comparable<_OuterIter> && equality_comparable<_InnerIter> + requires _Deref_is_glvalue && forward_range<_Base> && equality_comparable<_InnerIter> { if (_Left._Outer_it != _Right._Outer_it) { return false; @@ -4293,10 +4378,10 @@ namespace ranges { template _NODISCARD constexpr bool _Equal(const _Iterator<_OtherConst>& _It) const - noexcept(noexcept(_Fake_copy_init(_It._Outer_it == _Last))) { + noexcept(noexcept(_Fake_copy_init(_It._Get_outer() == _Last))) { _STL_INTERNAL_STATIC_ASSERT( sentinel_for, iterator_t<_Maybe_const<_OtherConst, _Vw>>>); - return _It._Outer_it == _Last; + return _It._Get_outer() == _Last; } public: @@ -4342,15 +4427,21 @@ namespace ranges { } _NODISCARD constexpr auto begin() { - constexpr bool _Use_const = _Simple_view<_Vw> && is_reference_v<_InnerRng> && _Simple_view<_Pat>; - return _Iterator<_Use_const>{*this, _RANGES begin(_Range)}; + if constexpr (forward_range<_Vw>) { + constexpr bool _Use_const = _Simple_view<_Vw> && is_reference_v<_InnerRng> && _Simple_view<_Pat>; + return _Iterator<_Use_const>{*this, _RANGES begin(_Range)}; + } else { + this->_Outer_it._Emplace(_RANGES begin(_Range)); + return _Iterator{*this}; + } } _NODISCARD constexpr auto begin() const #ifdef __clang__ // TRANSITION, LLVM-47414 requires _Can_const_join_with<_Vw, _Pat> #else // ^^^ workaround / no workaround vvv - requires input_range && forward_range && is_reference_v<_InnerRng> + requires forward_range && forward_range && is_reference_v<_InnerRng> + && input_range<_InnerRng> #endif // TRANSITION, LLVM-47414 { return _Iterator{*this, _RANGES begin(_Range)}; @@ -4371,11 +4462,11 @@ namespace ranges { #ifdef __clang__ // TRANSITION, LLVM-47414 requires _Can_const_join_with<_Vw, _Pat> #else // ^^^ workaround / no workaround vvv - requires input_range && forward_range && is_reference_v<_InnerRng> + requires forward_range && forward_range && is_reference_v<_InnerRng> + && input_range<_InnerRng> #endif // TRANSITION, LLVM-47414 { - if constexpr (forward_range && forward_range<_InnerRng> // - && common_range<_Vw> && common_range<_InnerRng>) { + if constexpr (forward_range<_InnerRng> && common_range<_Vw> && common_range<_InnerRng>) { return _Iterator{*this, _RANGES end(_Range)}; } else { return _Sentinel{*this}; @@ -6286,6 +6377,10 @@ namespace ranges { return (_STD min)(_Parent->_Remainder, _RANGES end(_Parent->_Range) - *_Parent->_Current); } + _NODISCARD constexpr const iterator_t<_Vw>& _Get_current() const noexcept { + return *_Parent->_Current; + } + public: using iterator_concept = input_iterator_tag; using difference_type = range_difference_t<_Vw>; @@ -6352,6 +6447,18 @@ namespace ranges { { return -_Left._Get_size(); } + + _NODISCARD_FRIEND constexpr range_rvalue_reference_t<_Vw> iter_move(const _Inner_iterator& _It) noexcept( + noexcept(_RANGES iter_move(_It._Get_current()))) { + return _RANGES iter_move(_It._Get_current()); + } + + friend constexpr void iter_swap(const _Inner_iterator& _Left, const _Inner_iterator& _Right) noexcept( + noexcept(_RANGES iter_swap(_Left._Get_current(), _Right._Get_current()))) + requires indirectly_swappable> + { + _RANGES iter_swap(_Left._Get_current(), _Right._Get_current()); + } }; class _Outer_iterator { @@ -8695,6 +8802,395 @@ namespace ranges { _EXPORT_STD inline constexpr _Zip_transform_fn zip_transform{}; } // namespace views + template + struct _Repeated_tuple_impl; + + template + struct _Repeated_tuple_impl<_Ty, index_sequence<_Indices...>> { + template + using _Repeat_type = _Ty; + + using type = tuple<_Repeat_type<_Indices>...>; + }; + + template + using _Repeated_tuple = typename _Repeated_tuple_impl<_Ty, make_index_sequence<_Nx>>::type; + + _EXPORT_STD template + requires view<_Vw> && (_Nx > 0) + class adjacent_view : public view_interface> { + private: + /* [[no_unique_address]] */ _Vw _Range{}; + + struct _As_sentinel { + explicit _As_sentinel() = default; + }; + + template + class _Iterator { + private: + friend adjacent_view; + + using _Base = _Maybe_const<_Const, _Vw>; + using _Base_iterator = iterator_t<_Base>; + + array<_Base_iterator, _Nx> _Current{}; + + constexpr _Iterator(_Base_iterator _First, sentinel_t<_Base> _Last) { + _Current.front() = _First; + for (size_t _Ix = 1; _Ix < _Nx; ++_Ix) { + _RANGES advance(_First, 1, _Last); + _Current[_Ix] = _First; + } + } + + constexpr _Iterator(_As_sentinel, _Base_iterator _First, _Base_iterator _Last) { + if constexpr (!bidirectional_range<_Base>) { + _Current.fill(_Last); + } else { + _Current.back() = _Last; + for (size_t _Ix = 1; _Ix < _Nx; ++_Ix) { + _RANGES advance(_Last, -1, _First); + _Current[_Nx - 1 - _Ix] = _Last; + } + } + } + + template + constexpr _Iterator(_Iterator& _Other, index_sequence<_Indices...>) noexcept( + is_nothrow_convertible_v, _Base_iterator>) + requires _Const && convertible_to, _Base_iterator> + : _Current{_STD move(_Other._Current[_Indices])...} {} + + public: + using iterator_category = input_iterator_tag; + using iterator_concept = conditional_t, random_access_iterator_tag, + conditional_t, bidirectional_iterator_tag, forward_iterator_tag>>; + using value_type = _Repeated_tuple, _Nx>; + using difference_type = range_difference_t<_Base>; + + _Iterator() = default; + + constexpr _Iterator(_Iterator _Other) noexcept( + is_nothrow_convertible_v, _Base_iterator>) // strengthened + requires _Const && convertible_to, _Base_iterator> + : _Iterator(_Other, make_index_sequence<_Nx>{}) {} + + _NODISCARD constexpr auto operator*() const { + return _RANGES _Tuple_transform([](auto& _It) -> decltype(auto) { return *_It; }, _Current); + } + + constexpr _Iterator& operator++() noexcept(noexcept(++_Current.front())) /* strengthened */ { + for (_Base_iterator& _It : _Current) { + ++_It; + } + return *this; + } + + constexpr _Iterator operator++(int) noexcept( + noexcept(++*this) && is_nothrow_copy_constructible_v<_Iterator>) /* strengthened */ { + auto _Tmp = *this; + ++*this; + return _Tmp; + } + + constexpr _Iterator& operator--() noexcept(noexcept(--_Current.back())) // strengthened + requires bidirectional_range<_Base> + { + for (_Base_iterator& _It : _Current) { + --_It; + } + return *this; + } + + constexpr _Iterator operator--(int) noexcept( + noexcept(--*this) && is_nothrow_copy_constructible_v<_Iterator>) // strengthened + requires bidirectional_range<_Base> + { + auto _Tmp = *this; + --*this; + return _Tmp; + } + + constexpr _Iterator& operator+=(const difference_type _Off) noexcept( + noexcept(_Current.back() += _Off)) // strengthened + requires random_access_range<_Base> + { + for (_Base_iterator& _It : _Current) { + _It += _Off; + } + return *this; + } + + constexpr _Iterator& operator-=(const difference_type _Off) noexcept( + noexcept(_Current.back() -= _Off)) // strengthened + requires random_access_range<_Base> + { + for (_Base_iterator& _It : _Current) { + _It -= _Off; + } + return *this; + } + + _NODISCARD constexpr auto operator[](const difference_type _Off) const + requires random_access_range<_Base> + { + return _RANGES _Tuple_transform([&](auto& _It) -> decltype(auto) { return _It[_Off]; }, _Current); + } + + _NODISCARD_FRIEND constexpr bool operator==(const _Iterator& _Left, const _Iterator& _Right) noexcept( + noexcept(_Fake_copy_init(_Left._Current.back() == _Right._Current.back()))) /* strengthened */ { + return _Left._Current.back() == _Right._Current.back(); + } + + _NODISCARD_FRIEND constexpr bool operator<(const _Iterator& _Left, const _Iterator& _Right) noexcept( + noexcept(_Fake_copy_init(_Left._Current.back() < _Right._Current.back()))) // strengthened + requires random_access_range<_Base> + { + return _Left._Current.back() < _Right._Current.back(); + } + + _NODISCARD_FRIEND constexpr bool operator>(const _Iterator& _Left, const _Iterator& _Right) noexcept( + noexcept(_Right < _Left)) // strengthened + requires random_access_range<_Base> + { + return _Right < _Left; + } + + _NODISCARD_FRIEND constexpr bool operator<=(const _Iterator& _Left, const _Iterator& _Right) noexcept( + noexcept(!(_Right < _Left))) // strengthened + requires random_access_range<_Base> + { + return !(_Right < _Left); + } + + _NODISCARD_FRIEND constexpr bool operator>=(const _Iterator& _Left, const _Iterator& _Right) noexcept( + noexcept(!(_Left < _Right))) // strengthened + requires random_access_range<_Base> + { + return !(_Left < _Right); + } + + _NODISCARD_FRIEND constexpr auto operator<=>(const _Iterator& _Left, const _Iterator& _Right) noexcept( + noexcept(_Left._Current.back() <=> _Right._Current.back())) // strengthened + requires random_access_range<_Base> && three_way_comparable<_Base_iterator> + { + return _Left._Current.back() <=> _Right._Current.back(); + } + + _NODISCARD_FRIEND constexpr _Iterator operator+(const _Iterator& _It, const difference_type _Off) noexcept( + noexcept(_STD declval<_Iterator&>() += _Off) + && is_nothrow_copy_constructible_v<_Iterator>) // strengthened + requires random_access_range<_Base> + { + auto _Tmp = _It; + _Tmp += _Off; + return _Tmp; + } + + _NODISCARD_FRIEND constexpr _Iterator operator+(const difference_type _Off, const _Iterator& _It) noexcept( + noexcept(_STD declval<_Iterator&>() += _Off) + && is_nothrow_copy_constructible_v<_Iterator>) // strengthened + requires random_access_range<_Base> + { + auto _Tmp = _It; + _Tmp += _Off; + return _Tmp; + } + + _NODISCARD_FRIEND constexpr _Iterator operator-(const _Iterator& _It, const difference_type _Off) noexcept( + noexcept(_STD declval<_Iterator&>() -= _Off) + && is_nothrow_copy_constructible_v<_Iterator>) // strengthened + requires random_access_range<_Base> + { + auto _Tmp = _It; + _Tmp -= _Off; + return _Tmp; + } + + _NODISCARD_FRIEND constexpr difference_type + operator-(const _Iterator& _Left, const _Iterator& _Right) noexcept( + noexcept(_Left._Current.back() - _Right._Current.back())) // strengthened + requires sized_sentinel_for<_Base_iterator, _Base_iterator> + { + return _Left._Current.back() - _Right._Current.back(); + } + + _NODISCARD_FRIEND constexpr auto iter_move(const _Iterator& _It) noexcept( + noexcept(_RANGES iter_move(_STD declval())) + && is_nothrow_move_constructible_v>) { + return _RANGES _Tuple_transform(_RANGES iter_move, _It._Current); + } + + friend constexpr void iter_swap(const _Iterator& _Left, const _Iterator& _Right) noexcept( + noexcept(_RANGES iter_swap(_STD declval<_Base_iterator>(), _STD declval<_Base_iterator>()))) + requires indirectly_swappable<_Base_iterator> + { + for (size_t _Ix = 0; _Ix < _Nx; ++_Ix) { + _RANGES iter_swap(_Left._Current[_Ix], _Right._Current[_Ix]); + } + } + }; + + template + class _Sentinel { + private: + friend adjacent_view; + + using _Base = _Maybe_const<_Const, _Vw>; + using _Base_sentinel = sentinel_t<_Base>; + + /* [[no_unique_address]] */ _Base_sentinel _End{}; + + constexpr explicit _Sentinel(_Base_sentinel _Last_) noexcept( + is_nothrow_move_constructible_v<_Base_sentinel>) // strengthened + : _End(_STD move(_Last_)) {} + + template + requires sentinel_for<_Base_sentinel, iterator_t<_Maybe_const<_OtherConst, _Vw>>> + _NODISCARD constexpr bool _Equal(const _Iterator<_OtherConst>& _It) const + noexcept(noexcept(_Fake_copy_init(_It._Current.back() == _End))) { + return _It._Current.back() == _End; + } + + template + requires sized_sentinel_for<_Base_sentinel, iterator_t<_Maybe_const<_OtherConst, _Vw>>> + _NODISCARD constexpr range_difference_t<_Maybe_const<_OtherConst, _Vw>> _Distance_from( + const _Iterator<_OtherConst>& _It) const noexcept(noexcept(_End - _It._Current.back())) { + return _End - _It._Current.back(); + } + + public: + _Sentinel() = default; + + constexpr _Sentinel(_Sentinel _Other) noexcept( + is_nothrow_convertible_v, _Base_sentinel>) // strengthened + requires _Const && convertible_to, _Base_sentinel> + : _End(_STD move(_Other._End)) {} + + template + requires sentinel_for<_Base_sentinel, iterator_t<_Maybe_const<_OtherConst, _Vw>>> + _NODISCARD_FRIEND constexpr bool operator==(const _Iterator<_OtherConst>& _It, + const _Sentinel& _Se) noexcept(noexcept(_Se._Equal(_It))) /* strengthened */ { + return _Se._Equal(_It); + } + + template + requires sized_sentinel_for<_Base_sentinel, iterator_t<_Maybe_const<_OtherConst, _Vw>>> + _NODISCARD_FRIEND constexpr range_difference_t<_Maybe_const<_OtherConst, _Vw>> operator-( + const _Iterator<_OtherConst>& _It, const _Sentinel& _Se) noexcept( // + noexcept(_Se._Distance_from(_It))) /* strengthened */ { + return -_Se._Distance_from(_It); + } + + template + requires sized_sentinel_for<_Base_sentinel, iterator_t<_Maybe_const<_OtherConst, _Vw>>> + _NODISCARD_FRIEND constexpr range_difference_t<_Maybe_const<_OtherConst, _Vw>> operator-( + const _Sentinel& _Se, const _Iterator<_OtherConst>& _It) noexcept( // + noexcept(_Se._Distance_from(_It))) /* strengthened */ { + return _Se._Distance_from(_It); + } + }; + + public: + // clang-format off + adjacent_view() requires default_initializable<_Vw> = default; + // clang-format on + + constexpr explicit adjacent_view(_Vw _Range_) noexcept(is_nothrow_move_constructible_v<_Vw>) // strengthened + : _Range(_STD move(_Range_)) {} + + _NODISCARD constexpr _Vw base() const& noexcept(is_nothrow_copy_constructible_v<_Vw>) // strengthened + requires copy_constructible<_Vw> + { + return _Range; + } + + _NODISCARD constexpr _Vw base() && noexcept(is_nothrow_move_constructible_v<_Vw>) /* strengthened */ { + return _STD move(_Range); + } + + _NODISCARD constexpr auto begin() + requires (!_Simple_view<_Vw>) + { + return _Iterator{_RANGES begin(_Range), _RANGES end(_Range)}; + } + + _NODISCARD constexpr auto begin() const + requires range + { + return _Iterator{_RANGES begin(_Range), _RANGES end(_Range)}; + } + + _NODISCARD constexpr auto end() + requires (!_Simple_view<_Vw>) + { + if constexpr (common_range<_Vw>) { + return _Iterator{_As_sentinel{}, _RANGES begin(_Range), _RANGES end(_Range)}; + } else { + return _Sentinel{_RANGES end(_Range)}; + } + } + + _NODISCARD constexpr auto end() const + requires range + { + if constexpr (common_range) { + return _Iterator{_As_sentinel{}, _RANGES begin(_Range), _RANGES end(_Range)}; + } else { + return _Sentinel{_RANGES end(_Range)}; + } + } + + _NODISCARD constexpr auto size() noexcept(noexcept(_RANGES size(_Range))) // strengthened + requires sized_range<_Vw> + { + using _Size_type = decltype(_RANGES size(_Range)); + using _Common_size_type = common_type_t<_Size_type, size_t>; + auto _Size = static_cast<_Common_size_type>(_RANGES size(_Range)); + _Size -= (_STD min)(_Size, static_cast<_Common_size_type>(_Nx - 1)); + return static_cast<_Size_type>(_Size); + } + + _NODISCARD constexpr auto size() const noexcept(noexcept(_RANGES size(_Range))) // strengthened + requires sized_range + { + using _Size_type = decltype(_RANGES size(_Range)); + using _Common_size_type = common_type_t<_Size_type, size_t>; + auto _Size = static_cast<_Common_size_type>(_RANGES size(_Range)); + _Size -= (_STD min)(_Size, static_cast<_Common_size_type>(_Nx - 1)); + return static_cast<_Size_type>(_Size); + } + }; + + template + inline constexpr bool enable_borrowed_range> = enable_borrowed_range<_Rng>; + + namespace views { + template + class _Adjacent_fn : public _Pipe::_Base<_Adjacent_fn<_Nx>> { + public: + template + _NODISCARD constexpr auto operator()(_Rng&& _Range) const + noexcept(noexcept(adjacent_view, _Nx>{_STD forward<_Rng>(_Range)})) + requires requires { adjacent_view, _Nx>{_STD forward<_Rng>(_Range)}; } + { + return adjacent_view, _Nx>{_STD forward<_Rng>(_Range)}; + } + + template + _NODISCARD constexpr auto operator()(_Rng&&) const noexcept + requires (_Nx == 0) + { + return empty_view>{}; + } + }; + + _EXPORT_STD template + inline constexpr _Adjacent_fn<_Nx> adjacent; + _EXPORT_STD inline constexpr _Adjacent_fn<2> pairwise; + } // namespace views + #ifdef __cpp_lib_ranges_to_container // clang-format off template diff --git a/stl/inc/regex b/stl/inc/regex index dc5c484cf54..4e7cc0036e0 100644 --- a/stl/inc/regex +++ b/stl/inc/regex @@ -983,9 +983,19 @@ public: using char_type = typename iterator_traits<_BidIt>::value_type; using string_type = basic_string; - match_results() : _Org(), _Ready(false) {} + match_results() = default; - explicit match_results(const _Alloc& _Al) : _Org(), _Ready(false), _Matches(_Al) {} + explicit match_results(const _Alloc& _Al) : _Matches(_Al) {} + + match_results(const match_results& _Other, const _Alloc& _Al) + : _Org(_Other._Org), _Ready(_Other._Ready), _Matches(_Other._Matches, _Al), _Prefix(_Other._Prefix), + _Suffix(_Other._Suffix), _Null_elem(_Other._Null_elem) {} + + match_results(match_results&& _Other, const _Alloc& _Al) noexcept( + conjunction_v>) // strengthened + : _Org(_STD move(_Other._Org)), _Ready(_Other._Ready), _Matches(_STD move(_Other._Matches), _Al), + _Prefix(_STD move(_Other._Prefix)), _Suffix(_STD move(_Other._Suffix)), + _Null_elem(_STD move(_Other._Null_elem)) {} _NODISCARD bool ready() const noexcept /* strengthened */ { return _Ready; @@ -1126,8 +1136,8 @@ public: return _Matches[_Sub]; } - _BidIt _Org; - bool _Ready; + _BidIt _Org = _BidIt(); + bool _Ready = false; private: _MyCont _Matches; @@ -2387,6 +2397,9 @@ public: using pointer = const value_type*; using reference = const value_type&; using iterator_category = forward_iterator_tag; +#if _HAS_CXX20 + using iterator_concept = input_iterator_tag; +#endif // _HAS_CXX20 regex_iterator() : _MyRe(nullptr) {} // construct end of sequence iterator @@ -2523,6 +2536,9 @@ public: using pointer = const value_type*; using reference = const value_type&; using iterator_category = forward_iterator_tag; +#if _HAS_CXX20 + using iterator_concept = input_iterator_tag; +#endif // _HAS_CXX20 regex_token_iterator() : _Res(nullptr) {} // construct end of sequence iterator diff --git a/stl/inc/type_traits b/stl/inc/type_traits index 463db6fd760..cd40abc7795 100644 --- a/stl/inc/type_traits +++ b/stl/inc/type_traits @@ -140,7 +140,7 @@ using add_rvalue_reference_t = typename _Add_reference<_Ty>::_Rvalue; _EXPORT_STD template add_rvalue_reference_t<_Ty> declval() noexcept { - static_assert(_Always_false<_Ty>, "Calling declval is ill-formed, see N4917 22.2.6 [declval]/2."); + static_assert(_Always_false<_Ty>, "Calling declval is ill-formed, see N4928 [declval]/2."); } _EXPORT_STD template @@ -1201,8 +1201,8 @@ using _Conditional_type = decltype(false ? _STD declval<_Ty1>() : _STD declval<_ template struct _Const_lvalue_cond_oper {}; -// N4917 21.3.8.7 [meta.trans.other]/3.3.4 (per the proposed resolution of LWG-3205): "Otherwise, if -// remove_cvref_t denotes a type..." +// N4928 [meta.trans.other]/3.3.4 (per the proposed resolution of LWG-3205): "Otherwise, if remove_cvref_t denotes +// a type..." template struct _Const_lvalue_cond_oper<_Ty1, _Ty2, void_t<_Conditional_type>> { using type = remove_cvref_t<_Conditional_type>; @@ -1279,11 +1279,11 @@ struct _Copy_cv_impl { using _Apply = const volatile _To; }; template -using _Copy_cv = // N4917 21.3.8.7 [meta.trans.other]/2.3 +using _Copy_cv = // N4928 [meta.trans.other]/2.3 typename _Copy_cv_impl<_From>::template _Apply<_To>; template -struct _Add_qualifiers { // _Add_qualifiers::template _Apply is XREF(A) from N4917 21.3.8.7 [meta.trans.other]/2.2 +struct _Add_qualifiers { // _Add_qualifiers::template _Apply is XREF(A) from N4928 [meta.trans.other]/2.2 template using _Apply = _Copy_cv<_Ty1, _Ty2>; }; @@ -1300,7 +1300,7 @@ struct _Add_qualifiers<_Ty1&&> { #if !defined(__EDG__) && !defined(__clang__) // TRANSITION, DevCom-10095944 template -using _Cond_res_if_right = // N4917 21.3.8.7 [meta.trans.other]/2.4 +using _Cond_res_if_right = // N4928 [meta.trans.other]/2.4 decltype(false ? _Returns_exactly<_Ty1>() : _Returns_exactly<_Ty2>()); template @@ -1322,7 +1322,7 @@ template using _Cond_res = typename _Cond_res_workaround<_Ty1, _Ty2>::type; #else // ^^^ workaround / no workaround vvv template -using _Cond_res = // N4917 21.3.8.7 [meta.trans.other]/2.4 +using _Cond_res = // N4928 [meta.trans.other]/2.4 decltype(false ? _Returns_exactly<_Ty1>() : _Returns_exactly<_Ty2>()); #endif // ^^^ no workaround ^^^ @@ -1332,30 +1332,30 @@ struct common_reference; _EXPORT_STD template using common_reference_t = typename common_reference<_Types...>::type; -// N4917 21.3.8.7 [meta.trans.other]/5.1: "If sizeof...(T) is zero ..." +// N4928 [meta.trans.other]/5.1: "If sizeof...(T) is zero ..." template <> struct common_reference<> {}; -// N4917 21.3.8.7 [meta.trans.other]/5.2: "...if sizeof...(T) is one ..." +// N4928 [meta.trans.other]/5.2: "...if sizeof...(T) is one ..." template struct common_reference<_Ty> { using type = _Ty; }; -// N4917 21.3.8.7 [meta.trans.other]/5.3: "...if sizeof...(T) is two..." +// N4928 [meta.trans.other]/5.3: "...if sizeof...(T) is two..." -// N4917 21.3.8.7 [meta.trans.other]/5.3.4: "if common_type_t is well-formed..." -// N4917 21.3.8.7 [meta.trans.other]/5.3.5: "Otherwise, there shall be no member type." +// N4928 [meta.trans.other]/5.3.4: "if common_type_t is well-formed..." +// N4928 [meta.trans.other]/5.3.5: "Otherwise, there shall be no member type." template struct _Common_reference2C : common_type<_Ty1, _Ty2> {}; -// N4917 21.3.8.7 [meta.trans.other]/5.3.3: "if COND_RES(T1, T2) is well-formed..." +// N4928 [meta.trans.other]/5.3.3: "if COND_RES(T1, T2) is well-formed..." template struct _Common_reference2C<_Ty1, _Ty2, void_t<_Cond_res<_Ty1, _Ty2>>> { using type = _Cond_res<_Ty1, _Ty2>; }; -// N4917 21.3.8.7 [meta.trans.other]/5.3.2: "if basic_common_reference<[...]>::type is well-formed..." +// N4928 [meta.trans.other]/5.3.2: "if basic_common_reference<[...]>::type is well-formed..." template using _Basic_specialization = typename basic_common_reference, remove_cvref_t<_Ty2>, _Add_qualifiers<_Ty1>::template _Apply, _Add_qualifiers<_Ty2>::template _Apply>::type; @@ -1368,7 +1368,10 @@ struct _Common_reference2B<_Ty1, _Ty2, void_t<_Basic_specialization<_Ty1, _Ty2>> using type = _Basic_specialization<_Ty1, _Ty2>; }; -// N4917 21.3.8.7 [meta.trans.other]/5.3.1: "If T1 and T2 are reference types and COMMON_REF(T1, T2) is well-formed..." +// N4928 [meta.trans.other]/5.3.1 as updated by P2655R3 (TRANSITION, cite new WP here): +// "Let R be COMMON-REF(T1, T2). If T1 and T2 are reference types, R is well-formed, and +// is_convertible_v, add_pointer_t> && is_convertible_v, add_pointer_t> +// is true, then the member typedef type denotes R." template struct _Common_reference2A : _Common_reference2B<_Ty1, _Ty2> {}; @@ -1376,37 +1379,48 @@ template enable_if_t, int> = 0> using _LL_common_ref = _Result; +template +struct _Common_reference2AX {}; + template -struct _Common_reference2A<_Ty1&, _Ty2&, void_t<_LL_common_ref<_Ty1, _Ty2>>> { - using type = _LL_common_ref<_Ty1, _Ty2>; // "both lvalues" case from N4917 21.3.8.7 [meta.trans.other]/2.5 +struct _Common_reference2AX<_Ty1&, _Ty2&, void_t<_LL_common_ref<_Ty1, _Ty2>>> { + using type = _LL_common_ref<_Ty1, _Ty2>; // "both lvalues" case from N4928 [meta.trans.other]/2.5 }; template -struct _Common_reference2A<_Ty1&&, _Ty2&, enable_if_t>>> { - using type = - _LL_common_ref; // "rvalue and lvalue" case from N4917 21.3.8.7 [meta.trans.other]/2.7 +struct _Common_reference2AX<_Ty1&&, _Ty2&, enable_if_t>>> { + using type = _LL_common_ref; // "rvalue and lvalue" case from N4928 [meta.trans.other]/2.7 }; template -struct _Common_reference2A<_Ty1&, _Ty2&&, enable_if_t>>> { - using type = - _LL_common_ref; // "lvalue and rvalue" case from N4917 21.3.8.7 [meta.trans.other]/2.8 +struct _Common_reference2AX<_Ty1&, _Ty2&&, enable_if_t>>> { + using type = _LL_common_ref; // "lvalue and rvalue" case from N4928 [meta.trans.other]/2.8 }; template using _RR_common_ref = remove_reference_t<_LL_common_ref<_Ty1, _Ty2>>&&; template -struct _Common_reference2A<_Ty1&&, _Ty2&&, +struct _Common_reference2AX<_Ty1&&, _Ty2&&, enable_if_t> && is_convertible_v<_Ty2&&, _RR_common_ref<_Ty1, _Ty2>>>> { - using type = _RR_common_ref<_Ty1, _Ty2>; // "both rvalues" case from N4917 21.3.8.7 [meta.trans.other]/2.6 + using type = _RR_common_ref<_Ty1, _Ty2>; // "both rvalues" case from N4928 [meta.trans.other]/2.6 +}; + +template +using _Common_ref_2AX_t = typename _Common_reference2AX<_Ty1, _Ty2>::type; + +template +struct _Common_reference2A<_Ty1, _Ty2, + enable_if_t, add_pointer_t<_Common_ref_2AX_t<_Ty1, _Ty2>>> + && is_convertible_v, add_pointer_t<_Common_ref_2AX_t<_Ty1, _Ty2>>>>> { + using type = _Common_ref_2AX_t<_Ty1, _Ty2>; }; template struct common_reference<_Ty1, _Ty2> : _Common_reference2A<_Ty1, _Ty2> {}; -// N4917 21.3.8.7 [meta.trans.other]/5.4: "if sizeof...(T) is greater than two..." +// N4928 [meta.trans.other]/5.4: "if sizeof...(T) is greater than two..." template struct _Fold_common_reference {}; template @@ -1461,9 +1475,8 @@ _EXPORT_STD template class reference_wrapper; // std::invoke isn't constexpr in C++17, and normally implementers are forbidden from "strengthening" constexpr -// (WG21-N4917 16.4.6.7 [constexpr.functions]/1), yet both std::apply and std::visit are required to be constexpr and -// have invoke-like behavior. As a result, we've chosen to apply the part of P1065R2 resolving LWG-2894 as a defect -// report. +// (N4928 [constexpr.functions]/1), yet both std::apply and std::visit are required to be constexpr and have +// invoke-like behavior. As a result, we've chosen to apply the part of P1065R2 resolving LWG-2894 as a defect report. enum class _Invoker_strategy { _Functor, @@ -2082,6 +2095,37 @@ struct _Is_trivially_swappable : bool_constant<_Is_trivially_swappable_v<_Ty>> { // true_type if and only if it is valid to swap two _Ty lvalues by exchanging object representations. }; +#ifdef __cpp_lib_concepts // TRANSITION, GH-395 +_EXPORT_STD template +concept convertible_to = +#if !defined(__EDG__) && !defined(__clang__) // TRANSITION, DevCom-1627396 + is_convertible_v<_From, _To> +#else // ^^^ workaround / no workaround vvv + __is_convertible_to(_From, _To) +#endif // ^^^ no workaround ^^^ + && requires { static_cast<_To>(_STD declval<_From>()); }; + +template +concept _Ref_wrap_common_reference_exists_with = + _Is_specialization_v<_RefWrap, reference_wrapper> + && requires { typename common_reference_t; } + && convertible_to<_RefWrapQ, common_reference_t>; + +template class _RefWrapQual, template class _TyQual> + requires (_Ref_wrap_common_reference_exists_with<_RefWrap, _Ty, _RefWrapQual<_RefWrap>, _TyQual<_Ty>> + && !_Ref_wrap_common_reference_exists_with<_Ty, _RefWrap, _TyQual<_Ty>, _RefWrapQual<_RefWrap>>) +struct basic_common_reference<_RefWrap, _Ty, _RefWrapQual, _TyQual> { + using type = common_reference_t>; +}; + +template class _TyQual, template class _RefWrapQual> + requires (_Ref_wrap_common_reference_exists_with<_RefWrap, _Ty, _RefWrapQual<_RefWrap>, _TyQual<_Ty>> + && !_Ref_wrap_common_reference_exists_with<_Ty, _RefWrap, _TyQual<_Ty>, _RefWrapQual<_RefWrap>>) +struct basic_common_reference<_Ty, _RefWrap, _TyQual, _RefWrapQual> { + using type = common_reference_t>; +}; +#endif // __cpp_lib_concepts + #define _BITMASK_OPS(_MAYBE_EXPORT, _BITMASK) \ _MAYBE_EXPORT _NODISCARD constexpr _BITMASK operator&(_BITMASK _Left, _BITMASK _Right) noexcept { \ using _IntTy = _STD underlying_type_t<_BITMASK>; \ diff --git a/stl/inc/utility b/stl/inc/utility index 1ad3d703bcf..f3bfe433fee 100644 --- a/stl/inc/utility +++ b/stl/inc/utility @@ -208,9 +208,9 @@ concept _Pair_like = _Tuple_like<_Ty> && tuple_size_v> == 2; #ifdef __clang__ // TRANSITION, LLVM-59827 template -concept _Can_construct_from_pair_like = - _Pair_like<_PairLike> && is_constructible_v<_Ty1, decltype(_STD get<0>(_STD declval<_PairLike>()))> - && is_constructible_v<_Ty2, decltype(_STD get<1>(_STD declval<_PairLike>()))>; +concept _Can_construct_from_pair_like = _Pair_like<_PairLike> && (!_Is_subrange_v>) + && is_constructible_v<_Ty1, decltype(_STD get<0>(_STD declval<_PairLike>()))> + && is_constructible_v<_Ty2, decltype(_STD get<1>(_STD declval<_PairLike>()))>; #endif // __clang__ #endif // _HAS_CXX23 #endif // __cpp_lib_concepts @@ -288,7 +288,8 @@ struct pair { // store a pair of values template , int> = 0> #else // ^^^ workaround / no workaround vvv template <_Pair_like _Other> - requires conjunction_v(_STD declval<_Other>()))>, + requires conjunction_v>>, + is_constructible<_Ty1, decltype(_STD get<0>(_STD declval<_Other>()))>, is_constructible<_Ty2, decltype(_STD get<1>(_STD declval<_Other>()))>> #endif // __clang__ constexpr explicit(!conjunction_v(_STD declval<_Other>())), _Ty1>, diff --git a/stl/inc/xmemory b/stl/inc/xmemory index 4ab17c0a8e2..c809f772e96 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -2304,8 +2304,13 @@ _NODISCARD constexpr auto uses_allocator_construction_args( const _Alloc& _Al, const pair<_Uty1, _Uty2>&& _Pair) noexcept; #endif // _HAS_CXX23 +#if _HAS_CXX23 && defined(__cpp_lib_concepts) // TRANSITION, GH-395 +_EXPORT_STD template + requires _Is_cv_pair<_Ty> && (_Pair_like<_Uty> || !_Is_deducible_as_pair<_Uty&>) +#else // ^^^ C++23 with concepts / C++20 or no concepts vvv _EXPORT_STD template && !_Is_deducible_as_pair<_Uty&>, int> = 0> +#endif // ^^^ C++20 or no concepts ^^^ _NODISCARD constexpr auto uses_allocator_construction_args(const _Alloc& _Al, _Uty&& _Ux) noexcept; _EXPORT_STD template , int> = 0> @@ -2390,41 +2395,60 @@ _NODISCARD constexpr auto uses_allocator_construction_args( } #endif // _HAS_CXX23 +#if _HAS_CXX23 && defined(__cpp_lib_concepts) // TRANSITION, GH-395 +_EXPORT_STD template + requires _Is_cv_pair<_Ty> && (_Pair_like<_Uty> || !_Is_deducible_as_pair<_Uty&>) +#else // ^^^ C++23 with concepts / C++20 or no concepts vvv _EXPORT_STD template && !_Is_deducible_as_pair<_Uty&>, int>> +#endif // ^^^ C++20 or no concepts ^^^ _NODISCARD constexpr auto uses_allocator_construction_args(const _Alloc& _Al, _Uty&& _Ux) noexcept { - struct _Pair_remaker { - const _Alloc& _Al; - _Uty& _Ux; - - constexpr operator remove_cv_t<_Ty>() const { - using _Pair_t = remove_cv_t<_Ty>; - static_assert(_Is_normally_bindable<_Pair_t, _Uty>, - "The argument must be bindable to a reference to the std::pair type."); - - using _Pair_first_t = typename _Pair_t::first_type; - using _Pair_second_t = typename _Pair_t::second_type; - using _Pair_ref_t = _Normally_bound_ref<_Pair_t, _Uty>; - _Pair_ref_t _Pair_ref = _STD forward<_Uty>(_Ux); - if constexpr (is_same_v<_Pair_ref_t, const _Pair_t&>) { - // equivalent to - // return _STD make_obj_using_allocator<_Pair_t>(_Al, _Pair_ref); - return _Pair_t{piecewise_construct, - _STD uses_allocator_construction_args<_Pair_first_t>(_Al, _Pair_ref.first), - _STD uses_allocator_construction_args<_Pair_second_t>(_Al, _Pair_ref.second)}; - } else { - // equivalent to - // return _STD make_obj_using_allocator<_Pair_t>(_Al, _STD move(_Pair_ref)); - return _Pair_t{piecewise_construct, - _STD uses_allocator_construction_args<_Pair_first_t>(_Al, _STD get<0>(_STD move(_Pair_ref))), - _STD uses_allocator_construction_args<_Pair_second_t>(_Al, _STD get<1>(_STD move(_Pair_ref)))}; +#if _HAS_CXX23 && defined(__cpp_lib_concepts) + if constexpr (_Pair_like<_Uty> && !_Is_subrange_v>) { + // equivalent to + // return _STD uses_allocator_construction_args<_Ty>(_Al, piecewise_construct, + // _STD forward_as_tuple(_STD get<0>(_STD forward<_Uty>(_Ux)), + // _STD forward_as_tuple(_STD get<1>(_STD forward<_Uty>(_Ux))); + return _STD make_tuple(piecewise_construct, + _STD uses_allocator_construction_args(_Al, _STD get<0>(_STD forward<_Uty>(_Ux))), + _STD uses_allocator_construction_args( + _Al, _STD get<1>(_STD forward<_Uty>(_Ux)))); + } else +#endif // _HAS_CXX23 && defined(__cpp_lib_concepts) + { + struct _Pair_remaker { + const _Alloc& _Al; + _Uty& _Ux; + + constexpr operator remove_cv_t<_Ty>() const { + using _Pair_t = remove_cv_t<_Ty>; + static_assert(_Is_normally_bindable<_Pair_t, _Uty>, + "The argument must be bindable to a reference to the std::pair type."); + + using _Pair_first_t = typename _Pair_t::first_type; + using _Pair_second_t = typename _Pair_t::second_type; + using _Pair_ref_t = _Normally_bound_ref<_Pair_t, _Uty>; + _Pair_ref_t _Pair_ref = _STD forward<_Uty>(_Ux); + if constexpr (is_same_v<_Pair_ref_t, const _Pair_t&>) { + // equivalent to + // return _STD make_obj_using_allocator<_Pair_t>(_Al, _Pair_ref); + return _Pair_t{piecewise_construct, + _STD uses_allocator_construction_args<_Pair_first_t>(_Al, _Pair_ref.first), + _STD uses_allocator_construction_args<_Pair_second_t>(_Al, _Pair_ref.second)}; + } else { + // equivalent to + // return _STD make_obj_using_allocator<_Pair_t>(_Al, _STD move(_Pair_ref)); + return _Pair_t{piecewise_construct, + _STD uses_allocator_construction_args<_Pair_first_t>(_Al, _STD get<0>(_STD move(_Pair_ref))), + _STD uses_allocator_construction_args<_Pair_second_t>(_Al, _STD get<1>(_STD move(_Pair_ref)))}; + } } - } - }; + }; - // equivalent to - // return _STD make_tuple(_Pair_remaker{_Al, _Ux}); - return tuple<_Pair_remaker>({_Al, _Ux}); + // equivalent to + // return _STD make_tuple(_Pair_remaker{_Al, _Ux}); + return tuple<_Pair_remaker>({_Al, _Ux}); + } } _EXPORT_STD template diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 0837c68a0e4..0b6cdf1a024 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -288,7 +288,9 @@ // P2588R3 barrier's Phase Completion Guarantees // P2602R2 Poison Pills Are Too Toxic // P2609R3 Relaxing Ranges Just A Smidge +// P2655R3 common_reference_t Of reference_wrapper Should Be A Reference Type // P2711R1 Making Multi-Param Constructors Of Views explicit +// P2770R0 Stashing Stashing Iterators For Proper Flattening // _HAS_CXX20 indirectly controls: // P0619R4 Removing C++17-Deprecated Features @@ -333,7 +335,7 @@ // P2291R3 constexpr Integral // P2302R4 ranges::contains, ranges::contains_subrange // P2321R2 zip -// (missing views::adjacent and views::adjacent_transform) +// (missing views::adjacent_transform) // P2322R6 ranges::fold_left, ranges::fold_right, Etc. // P2387R3 Pipe Support For User-Defined Range Adaptors // P2404R3 Move-Only Types For Comparison Concepts @@ -819,7 +821,7 @@ #define _CPPLIB_VER 650 #define _MSVC_STL_VERSION 143 -#define _MSVC_STL_UPDATE 202302L +#define _MSVC_STL_UPDATE 202303L #ifndef _ALLOW_COMPILER_AND_STL_VERSION_MISMATCH #if defined(__CUDACC__) && defined(__CUDACC_VER_MAJOR__) @@ -1611,20 +1613,26 @@ _EMIT_STL_ERROR(STL1004, "C++98 unexpected() is incompatible with C++23 unexpect #define __cpp_lib_bit_cast 201806L #define __cpp_lib_bitops 201907L #define __cpp_lib_bounded_array_traits 201902L -#define __cpp_lib_constexpr_algorithms 201806L -#define __cpp_lib_constexpr_complex 201711L -#define __cpp_lib_constexpr_dynamic_alloc 201907L -#define __cpp_lib_constexpr_functional 201907L -#define __cpp_lib_constexpr_iterator 201811L -#define __cpp_lib_constexpr_numeric 201911L -#define __cpp_lib_constexpr_string 201907L -#define __cpp_lib_constexpr_string_view 201811L -#define __cpp_lib_constexpr_tuple 201811L -#define __cpp_lib_constexpr_utility 201811L -#define __cpp_lib_constexpr_vector 201907L -#define __cpp_lib_destroying_delete 201806L -#define __cpp_lib_endian 201907L -#define __cpp_lib_erase_if 202002L + +#ifdef __cpp_lib_concepts +#define __cpp_lib_common_reference 202302L +#define __cpp_lib_common_reference_wrapper 202302L +#endif // __cpp_lib_concepts + +#define __cpp_lib_constexpr_algorithms 201806L +#define __cpp_lib_constexpr_complex 201711L +#define __cpp_lib_constexpr_dynamic_alloc 201907L +#define __cpp_lib_constexpr_functional 201907L +#define __cpp_lib_constexpr_iterator 201811L +#define __cpp_lib_constexpr_numeric 201911L +#define __cpp_lib_constexpr_string 201907L +#define __cpp_lib_constexpr_string_view 201811L +#define __cpp_lib_constexpr_tuple 201811L +#define __cpp_lib_constexpr_utility 201811L +#define __cpp_lib_constexpr_vector 201907L +#define __cpp_lib_destroying_delete 201806L +#define __cpp_lib_endian 201907L +#define __cpp_lib_erase_if 202002L #ifdef __cpp_lib_concepts #define __cpp_lib_format 202207L diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index cb9206752bf..ee34f04da44 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -141,6 +141,15 @@ std/ranges/range.access/rbegin.pass.cpp FAIL std/ranges/range.access/rend.pass.cpp FAIL std/ranges/range.access/size.pass.cpp FAIL +# libc++ doesn't implement P2770R0 "Stashing stashing iterators for proper flattening" +std/ranges/range.adaptors/range.join.view/end.pass.cpp FAIL +std/ranges/range.adaptors/range.join.view/iterator/ctor.other.pass.cpp FAIL +std/ranges/range.adaptors/range.join.view/iterator/ctor.parent.outer.pass.cpp FAIL +std/ranges/range.adaptors/range.join.view/iterator/eq.pass.cpp FAIL +std/ranges/range.adaptors/range.join.view/sentinel/eq.pass.cpp FAIL +std/re/re.iter/re.regiter/iterator_concept_conformance.compile.pass.cpp FAIL +std/re/re.iter/re.tokiter/iterator_concept_conformance.compile.pass.cpp FAIL + # libc++ doesn't implement LWG-3865 Sorting a range of pairs std/utilities/tuple/tuple.tuple/tuple.apply/make_from_tuple.pass.cpp FAIL diff --git a/tests/libcxx/usual_matrix.lst b/tests/libcxx/usual_matrix.lst index 4a66aa7f1f1..5604d27fed6 100644 --- a/tests/libcxx/usual_matrix.lst +++ b/tests/libcxx/usual_matrix.lst @@ -3,7 +3,7 @@ RUNALL_INCLUDE ..\universal_prefix.lst RUNALL_CROSSLIST -PM_CL="/EHsc /MTd /std:c++latest /permissive- /utf-8 /FImsvc_stdlib_force_include.h /wd4643 /D_STL_CALL_ABORT_INSTEAD_OF_INVALID_PARAMETER /D_USE_JOIN_VIEW_INPUT_RANGE" +PM_CL="/EHsc /MTd /std:c++latest /permissive- /utf-8 /FImsvc_stdlib_force_include.h /wd4643 /D_STL_CALL_ABORT_INSTEAD_OF_INVALID_PARAMETER" RUNALL_CROSSLIST PM_CL="/analyze:autolog- /Zc:preprocessor /wd6262" PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call" diff --git a/tests/std/include/range_algorithm_support.hpp b/tests/std/include/range_algorithm_support.hpp index e6b629025b2..95f2b940114 100644 --- a/tests/std/include/range_algorithm_support.hpp +++ b/tests/std/include/range_algorithm_support.hpp @@ -112,7 +112,7 @@ namespace test { enum class CanDifference : bool { no, yes }; enum class CanCompare : bool { no, yes }; - enum class ProxyRef { no, yes, prvalue }; + enum class ProxyRef { no, yes, prvalue, xvalue }; enum class WrappedState { wrapped, unwrapped, @@ -394,8 +394,9 @@ namespace test { template struct init_list_not_constructible_iterator { - using difference_type = int; - using value_type = T; + using iterator_category = std::forward_iterator_tag; + using difference_type = int; + using value_type = T; init_list_not_constructible_iterator() = default; init_list_not_constructible_iterator(T*) {} @@ -407,10 +408,11 @@ namespace test { init_list_not_constructible_iterator& operator++(); // not defined init_list_not_constructible_iterator operator++(int); // not defined + bool operator==(init_list_not_constructible_iterator) const; // not defined bool operator==(init_list_not_constructible_sentinel) const; // not defined }; - static_assert(std::input_iterator>); + static_assert(std::forward_iterator>); static_assert( std::sentinel_for, init_list_not_constructible_iterator>); @@ -433,7 +435,8 @@ namespace test { static constexpr bool at_least = derived_from; using ReferenceType = conditional_t, - conditional_t, Element&>>; + conditional_t, + conditional_t>>; struct post_increment_proxy { Element* ptr_; @@ -475,7 +478,7 @@ namespace test { } [[nodiscard]] constexpr ReferenceType operator*() const noexcept { - return ReferenceType{*ptr_}; + return static_cast(*ptr_); } template diff --git a/tests/std/test.lst b/tests/std/test.lst index 157fecb6688..fc8574d2405 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -568,6 +568,7 @@ tests\P2278R4_views_as_const tests\P2302R4_ranges_alg_contains tests\P2302R4_ranges_alg_contains_subrange tests\P2321R2_proxy_reference +tests\P2321R2_views_adjacent tests\P2321R2_views_zip tests\P2321R2_views_zip_transform tests\P2322R6_ranges_alg_fold diff --git a/tests/std/tests/P0220R1_polymorphic_memory_resources/test.cpp b/tests/std/tests/P0220R1_polymorphic_memory_resources/test.cpp index 0de48395da2..d981d148031 100644 --- a/tests/std/tests/P0220R1_polymorphic_memory_resources/test.cpp +++ b/tests/std/tests/P0220R1_polymorphic_memory_resources/test.cpp @@ -1546,6 +1546,19 @@ namespace { al.construct(static_cast(raw_ptr), mem_pair_conv{}); } } // namespace map_containers + + void test_gh3408() { + // We ignored the possibility that max_blocks_per_chunk could be less than _Default_next_capacity + recording_resource upstream; + std::pmr::pool_options options{}; + options.max_blocks_per_chunk = 1; + std::pmr::unsynchronized_pool_resource res{options, &upstream}; + const std::size_t size = 0x8009; + (void) res.allocate(size); + const allocation& alloc = upstream.allocations_[upstream.allocations_.size() - 1]; + CHECK(alloc.size >= 0x10000); + CHECK(alloc.size < 2 * 0x10000); + } } // unnamed namespace int main() { @@ -1591,4 +1604,6 @@ int main() { map_containers::test(); map_containers::lwg3677_test(); + + test_gh3408(); } diff --git a/tests/std/tests/P0475R1_P0591R4_uses_allocator_construction/test.cpp b/tests/std/tests/P0475R1_P0591R4_uses_allocator_construction/test.cpp index 351fa0edbb5..352327d2a47 100644 --- a/tests/std/tests/P0475R1_P0591R4_uses_allocator_construction/test.cpp +++ b/tests/std/tests/P0475R1_P0591R4_uses_allocator_construction/test.cpp @@ -177,6 +177,43 @@ constexpr bool test_P0591R4() { } #endif // _HAS_CXX23 +#if _HAS_CXX23 && defined(__cpp_lib_concepts) // TRANSITION, GH-395 + { // pair(PairLike&&) overload + tuple tpl(i, i); + auto tuple14 = uses_allocator_construction_args>(alloc, tpl); + static_assert( + is_same_v, AllocatorArgConstructArgs>>); + + auto tuple15 = uses_allocator_construction_args>(alloc, tpl); + static_assert(is_same_v>>); + + auto tuple16 = uses_allocator_construction_args>(alloc, move(tpl)); + static_assert( + is_same_v, MovedAllocatorArgConstructArgs>>); + + auto tuple17 = uses_allocator_construction_args>(alloc, move(tpl)); + static_assert( + is_same_v>>); + + auto tuple18 = uses_allocator_construction_args>(alloc, as_const(tpl)); + static_assert(is_same_v, ConstAllocatorArgConstructArgs>>); + + auto tuple19 = uses_allocator_construction_args>(alloc, as_const(tpl)); + static_assert( + is_same_v>>); + + auto tuple20 = + uses_allocator_construction_args>(alloc, move(as_const(tpl))); + static_assert(is_same_v, MovedConstAllocatorArgConstructArgs>>); + + auto tuple21 = uses_allocator_construction_args>(alloc, move(as_const(tpl))); + static_assert(is_same_v>>); + } +#endif // _HAS_CXX23 && defined(__cpp_lib_concepts) + { auto obj1 = make_obj_using_allocator(alloc, i); static_assert(is_same_v); diff --git a/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp b/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp index fabf6cde482..6c02ad97a89 100644 --- a/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp +++ b/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp @@ -1356,10 +1356,13 @@ void libfmt_formatter_test_runtime_width() { throw_helper(STR("{0:{1}}"), 0, (int_max + 1u)); throw_helper(STR("{0:{1}}"), 0, -1l); throw_helper(STR("{0:{1}}"), 0, (int_max + 1ul)); - assert(format(STR("{0:{1}}"), 0, '0') - == STR(" 0")); // behavior differs from libfmt, but conforms throw_helper(STR("{0:{1}}"), 0, 0.0); + // LWG-3720: Restrict the valid types of arg-id for width and precision in std-format-spec + throw_helper(STR("{:*^{}}"), 'a', true); + throw_helper(STR("{:*^{}}"), 'a', '0'); + assert(format(STR("{:*^{}}"), 'a', static_cast(2)) == STR("a*")); + assert(format(STR("{0:{1}}"), 42, 0) == STR("42")); // LWG-3721: zero dynamic width is OK assert(format(STR("{0:{1}}"), -42, 4) == STR(" -42")); @@ -1407,6 +1410,11 @@ void libfmt_formatter_test_runtime_precision() { throw_helper(STR("{0:.{1}}"), reinterpret_cast(0xcafe), 2); throw_helper(STR("{0:.{1}f}"), reinterpret_cast(0xcafe), 2); assert(format(STR("{0:.{1}}"), STR("str"), 2) == STR("st")); + + // LWG-3720: Restrict the valid types of arg-id for width and precision in std-format-spec + throw_helper(STR("{:.{}f}"), 3.14f, true); + throw_helper(STR("{:.{}f}"), 3.14f, '0'); + assert(format(STR("{:.{}f}"), 3.14f, static_cast(2)) == STR("3.14")); } template diff --git a/tests/std/tests/P0896R4_views_elements/test.cpp b/tests/std/tests/P0896R4_views_elements/test.cpp index 1bff24a240a..61a76c174f7 100644 --- a/tests/std/tests/P0896R4_views_elements/test.cpp +++ b/tests/std/tests/P0896R4_views_elements/test.cpp @@ -396,7 +396,7 @@ constexpr void instantiation_test() { // GH-3014 ": list-initialization is misused" void test_gh_3014() { // COMPILE-ONLY - struct InRange { + struct FwdRange { P* begin() { return nullptr; } @@ -410,7 +410,7 @@ void test_gh_3014() { // COMPILE-ONLY } }; - auto r = InRange{} | views::elements<0>; + auto r = FwdRange{} | views::elements<0>; [[maybe_unused]] decltype(as_const(r).begin()) i = r.begin(); // Check 'iterator(iterator i)' } diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index ba5949952d5..96d87e350f9 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -#define _USE_JOIN_VIEW_INPUT_RANGE - #include #include #include @@ -145,7 +143,10 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { // Validate join_view::begin static_assert(CanMemberBegin); - static_assert(CanMemberBegin == (input_range && is_reference_v>) ); + // clang-format off + static_assert(CanMemberBegin == (forward_range && is_reference_v> + && input_range>) ); + // clang-format on if (forward_range) { const iterator_t i = r.begin(); if (!is_empty) { @@ -179,8 +180,9 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { // Validate join_view::end static_assert(CanMemberEnd); - static_assert(CanMemberEnd == (input_range && is_reference_v>) ); // clang-format off + static_assert(CanMemberEnd == (forward_range && is_reference_v> + && input_range>) ); static_assert(common_range == (forward_range && is_reference_v> && common_range && forward_range && common_range) ); static_assert(common_range == (forward_range && is_reference_v> @@ -512,7 +514,7 @@ void test_non_trivially_destructible_type() { // COMPILE-ONLY // GH-3014 ": list-initialization is misused" void test_gh_3014() { // COMPILE-ONLY - struct InRange { + struct FwdRange { string* begin() { return nullptr; } @@ -526,10 +528,87 @@ void test_gh_3014() { // COMPILE-ONLY } }; - auto r = InRange{} | views::join; + auto r = FwdRange{} | views::join; [[maybe_unused]] decltype(as_const(r).begin()) i = r.begin(); // Check 'iterator(iterator i)' } +constexpr bool test_lwg3698() { + // LWG-3698 "regex_iterator and join_view don't work together very well" + struct stashing_iterator { + using difference_type = int; + using value_type = span; + + int x = 1; + + constexpr stashing_iterator& operator++() { + ++x; + return *this; + } + constexpr void operator++(int) { + ++x; + } + constexpr value_type operator*() const { + return {&x, &x + 1}; + } + constexpr bool operator==(default_sentinel_t) const { + return x > 3; + } + }; + + auto r = ranges::subrange{stashing_iterator{}, default_sentinel} | views::join; + auto r2 = r; + auto it = r.begin(); + auto it2 = r2.begin(); + + auto itcopy = it; + it = ++it2; + assert(*itcopy == 1); + + struct intricate_range { + constexpr stashing_iterator begin() { + return {}; + } + constexpr default_sentinel_t end() { + return {}; + } + constexpr const span* begin() const { + return ranges::begin(intervals); + } + constexpr const span* end() const { + return ranges::end(intervals); + } + }; + + auto jv = intricate_range{} | views::join; + auto cit = as_const(jv).begin(); + assert(*++cit == 1); + assert(*--cit == 0); + assert(ranges::equal(as_const(jv), expected_ints)); + + return true; +} + +void test_lwg3700() { // COMPILE-ONLY + // LWG-3700 "The const begin of the join_view family does not require InnerRng to be a range" + auto r = views::iota(0, 5) | views::filter([](auto) { return true; }); + auto j = views::single(r) | views::join; + using J = decltype(j); + STATIC_ASSERT(!CanMemberBegin); + STATIC_ASSERT(!CanMemberEnd); +} + +constexpr bool test_lwg3791() { + // LWG-3791 "join_view::iterator::operator-- may be ill-formed" + // Validate that join_view works when range_reference_t is an rvalue reference + using inner = test::range; + using outer = test::range; + + instantiator::call(); + + return true; +} + int main() { // Validate views constexpr string_view expected = "Hello World!"sv; @@ -622,4 +701,10 @@ int main() { STATIC_ASSERT(instantiation_test()); instantiation_test(); + + STATIC_ASSERT(test_lwg3698()); + assert(test_lwg3698()); + + STATIC_ASSERT(test_lwg3791()); + assert(test_lwg3791()); } diff --git a/tests/std/tests/P0896R4_views_transform/test.cpp b/tests/std/tests/P0896R4_views_transform/test.cpp index e314e7e6a33..57bfc5020cd 100644 --- a/tests/std/tests/P0896R4_views_transform/test.cpp +++ b/tests/std/tests/P0896R4_views_transform/test.cpp @@ -813,7 +813,7 @@ void test_gh_1709() { // GH-3014 ": list-initialization is misused" void test_gh_3014() { // COMPILE-ONLY - struct InRange { + struct FwdRange { int* begin() { return nullptr; } @@ -827,7 +827,7 @@ void test_gh_3014() { // COMPILE-ONLY } }; - auto r = InRange{} | views::transform(identity{}); + auto r = FwdRange{} | views::transform(identity{}); [[maybe_unused]] decltype(as_const(r).begin()) i = r.begin(); // Check 'iterator(iterator i)' } diff --git a/tests/std/tests/P1132R7_out_ptr/test.cpp b/tests/std/tests/P1132R7_out_ptr/test.cpp index 32cc7e14225..c73a1f0726b 100644 --- a/tests/std/tests/P1132R7_out_ptr/test.cpp +++ b/tests/std/tests/P1132R7_out_ptr/test.cpp @@ -76,8 +76,22 @@ void test_shared_ptr() { assert(*int_ptr == 32); } + // LWG-3734 Inconsistency in inout_ptr and out_ptr for empty case + { + const auto f = [](void** ptr) { *ptr = new int(42); }; + + { + auto temp_adaptor = out_ptr(int_ptr, deleter); + assert(int_ptr.get() == nullptr); + f(temp_adaptor); + } + + assert(count == 2); + assert(*int_ptr == 42); + } + int_ptr.reset(); - assert(count == 2); + assert(count == 3); } template @@ -119,6 +133,19 @@ void test_smart_ptr(Args&&... args) { assert(*int_ptr == 19); } + // LWG-3734 Inconsistency in inout_ptr and out_ptr for empty case + { + const auto f = [](void** ptr) { *ptr = new int(42); }; + + { + auto temp_adaptor = out_ptr(int_ptr); + assert(int_ptr.get() == nullptr); + f(temp_adaptor); + } + + assert(*int_ptr == 42); + } + // LWG-3594 inout_ptr - inconsistent release() in destructor { const auto f = [](int** ptr) { @@ -141,6 +168,10 @@ struct resettable_ptr { explicit resettable_ptr(int* p) : ptr(p) {} + void reset() { + ptr.reset(); + } + void reset(int* p, reset_tag) { ptr.reset(p); } @@ -163,6 +194,7 @@ struct constructible_ptr { unique_ptr ptr; + constructible_ptr() = default; explicit constructible_ptr(int* p) : ptr(p) {} explicit constructible_ptr(int* p, reset_tag) : ptr(p) {} diff --git a/tests/std/tests/P2164R9_views_enumerate/test.cpp b/tests/std/tests/P2164R9_views_enumerate/test.cpp index 77f6c90c185..942f853dde1 100644 --- a/tests/std/tests/P2164R9_views_enumerate/test.cpp +++ b/tests/std/tests/P2164R9_views_enumerate/test.cpp @@ -65,6 +65,7 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { // Validate borrowed_range STATIC_ASSERT(ranges::borrowed_range == ranges::borrowed_range); + // Validate range closure object // ... with lvalue argument STATIC_ASSERT(CanViewEnumerate == (!is_view || copy_constructible) ); if constexpr (CanViewEnumerate) { @@ -610,7 +611,7 @@ constexpr void instantiation_test() { #ifdef TEST_EVERYTHING test_in(); #else // ^^^ test all input range permutations / test only "interesting" permutations vvv - using test::Common, test::Sized, test::ProxyRef; + using test::Common, test::Sized; // When the base range is an input range, the view is sensitive to differencing instantiator::call>(); diff --git a/tests/std/tests/P2321R2_views_adjacent/env.lst b/tests/std/tests/P2321R2_views_adjacent/env.lst new file mode 100644 index 00000000000..8ac7033b206 --- /dev/null +++ b/tests/std/tests/P2321R2_views_adjacent/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\strict_concepts_latest_matrix.lst diff --git a/tests/std/tests/P2321R2_views_adjacent/test.cpp b/tests/std/tests/P2321R2_views_adjacent/test.cpp new file mode 100644 index 00000000000..bc993cf73bc --- /dev/null +++ b/tests/std/tests/P2321R2_views_adjacent/test.cpp @@ -0,0 +1,950 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; + +template +concept CanViewAdjacent = requires(R&& r) { views::adjacent(forward(r)); }; + +template +concept CanConstructAdjacentView = requires(R&& r) { ranges::adjacent_view, N>{forward(r)}; }; + +template +struct repeated_tuple_impl; + +template +struct repeated_tuple_impl> { + template + using repeat_type = T; + + using type = tuple...>; +}; + +template +using repeated_tuple = typename repeated_tuple_impl>::type; + +STATIC_ASSERT(same_as, tuple<>>); +STATIC_ASSERT(same_as, tuple>); +STATIC_ASSERT(same_as, tuple>); + +// Check views::pairwise +STATIC_ASSERT(same_as)>); + +template +constexpr bool test_one(Rng&& rng, Expected&& expected) { + using ranges::adjacent_view, ranges::forward_range, ranges::bidirectional_range, ranges::random_access_range, + ranges::sized_range, ranges::common_range, ranges::iterator_t, ranges::sentinel_t, ranges::const_iterator_t, + ranges::const_sentinel_t, ranges::range_difference_t, ranges::range_value_t, ranges::range_reference_t; + + constexpr bool is_view = ranges::view>; + + using V = views::all_t; + using R = adjacent_view; + + STATIC_ASSERT(ranges::view); + STATIC_ASSERT(ranges::input_range); + STATIC_ASSERT(forward_range); + STATIC_ASSERT(bidirectional_range == bidirectional_range); + STATIC_ASSERT(random_access_range == random_access_range); + STATIC_ASSERT(!ranges::contiguous_range); + + // Check default-initializability + STATIC_ASSERT(default_initializable == default_initializable); + + // Check borrowed_range + STATIC_ASSERT(ranges::borrowed_range == ranges::borrowed_range); + + // Check range closure object + constexpr auto closure = views::adjacent; + + // ... with lvalue argument + STATIC_ASSERT(CanViewAdjacent == (!is_view || copy_constructible) ); + if constexpr (CanViewAdjacent) { + constexpr bool is_noexcept = !is_view || is_nothrow_copy_constructible_v; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(closure(rng)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(rng | closure) == is_noexcept); + } + + // ... with const lvalue argument + STATIC_ASSERT(CanViewAdjacent&, N> == (!is_view || copy_constructible) ); + if constexpr (CanViewAdjacent&, N>) { + using RC = adjacent_view&>, N>; + constexpr bool is_noexcept = !is_view || is_nothrow_copy_constructible_v; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(closure(as_const(rng))) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(rng) | closure) == is_noexcept); + } + + // ... with rvalue argument + STATIC_ASSERT(CanViewAdjacent, N> == (is_view || movable>) ); + if constexpr (CanViewAdjacent, N>) { + using RS = adjacent_view>, N>; + constexpr bool is_noexcept = is_nothrow_move_constructible_v; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(closure(move(rng))) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(rng) | closure) == is_noexcept); + } + + // ... with const rvalue argument + STATIC_ASSERT(CanViewAdjacent, N> == (is_view && copy_constructible) ); + if constexpr (CanViewAdjacent, N>) { + constexpr bool is_noexcept = is_nothrow_copy_constructible_v; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(closure(move(as_const(rng)))) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(as_const(rng)) | closure) == is_noexcept); + } + + R r{forward(rng)}; + + // Check adjacent_view::size + STATIC_ASSERT(CanMemberSize == sized_range); + if constexpr (CanMemberSize) { + same_as> auto s = r.size(); + assert(_To_unsigned_like(s) == ranges::size(expected)); + STATIC_ASSERT(noexcept(r.size()) == noexcept(ranges::size(rng))); // strengthened + } + + // Check adjacent_view::size (const) + STATIC_ASSERT(CanMemberSize == sized_range); + if constexpr (CanMemberSize) { + same_as> auto s = as_const(r).size(); + assert(_To_unsigned_like(s) == ranges::size(expected)); + STATIC_ASSERT(noexcept(as_const(r).size()) == noexcept(ranges::size(as_const(rng)))); // strengthened + } + + const bool is_empty = ranges::empty(expected); + + // Check view_interface::empty and operator bool + STATIC_ASSERT(CanMemberEmpty); + STATIC_ASSERT(CanBool == CanEmpty); + if constexpr (CanMemberEmpty) { + assert(r.empty() == is_empty); + assert(static_cast(r) == !is_empty); + } + + // Check view_interface::empty and operator bool (const) + STATIC_ASSERT(CanMemberEmpty); + STATIC_ASSERT(CanBool == CanEmpty); + if constexpr (CanMemberEmpty) { + assert(as_const(r).empty() == is_empty); + assert(static_cast(as_const(r)) == !is_empty); + } + + // Check content + assert(ranges::equal(r, expected)); + + // Check adjacent_view::begin + STATIC_ASSERT(CanMemberBegin); + { + const same_as> auto i = r.begin(); + if (!is_empty) { + assert(*i == *begin(expected)); + } + + if constexpr (copy_constructible) { + auto r2 = r; + const same_as> auto i2 = r2.begin(); + if (!is_empty) { + assert(*i2 == *i); + } + } + } + + // Check adjacent_view::begin (const) + STATIC_ASSERT(CanMemberBegin == ranges::range); + if constexpr (CanMemberBegin) { + const same_as> auto ci = as_const(r).begin(); + if (!is_empty) { + assert(*ci == *begin(expected)); + } + + if constexpr (copy_constructible) { + const auto cr2 = r; + const same_as> auto ci2 = cr2.begin(); + if (!is_empty) { + assert(*ci2 == *ci); + } + } + } + + // Check adjacent_view::end + STATIC_ASSERT(CanMemberEnd); + { + const same_as> auto s = r.end(); + assert((r.begin() == s) == is_empty); + + if constexpr (sentinel_for, iterator_t>) { + assert((as_const(r).begin() == s) == is_empty); + } + + STATIC_ASSERT(common_range == common_range); + if constexpr (common_range && bidirectional_range) { + if (!is_empty) { + assert(*prev(s) == *prev(end(expected))); + } + + if constexpr (copy_constructible) { + auto r2 = r; + if (!is_empty) { + assert(*prev(r2.end()) == *prev(end(expected))); + } + } + } + } + + // Check adjacent_view::end (const) + STATIC_ASSERT(CanMemberEnd == ranges::range); + if constexpr (CanMemberEnd) { + const same_as> auto cs = as_const(r).end(); + assert((as_const(r).begin() == cs) == is_empty); + + if constexpr (sentinel_for, iterator_t>) { + assert((r.begin() == cs) == is_empty); + } + + STATIC_ASSERT(common_range == common_range); + if constexpr (common_range && bidirectional_range) { + if (!is_empty) { + assert(*prev(cs) == *prev(end(expected))); + } + + if constexpr (copy_constructible) { + const auto r2 = r; + if (!is_empty) { + assert(*prev(r2.end()) == *prev(end(expected))); + } + } + } + } + + // Check view_interface::cbegin + STATIC_ASSERT(CanMemberCBegin); + STATIC_ASSERT(CanMemberCBegin == ranges::range); + { + const same_as> auto i = r.cbegin(); + if (!is_empty) { + assert(*i == *cbegin(expected)); + } + + if constexpr (copyable) { + auto r2 = r; + const same_as> auto i2 = r2.cbegin(); + if (!is_empty) { + assert(*i2 == *i); + } + } + + if constexpr (CanCBegin) { + const same_as> auto i3 = as_const(r).cbegin(); + if (!is_empty) { + assert(*i3 == *i); + } + } + } + + // Check view_interface::cend + STATIC_ASSERT(CanMemberCEnd); + STATIC_ASSERT(CanMemberCEnd == ranges::range); + if (!is_empty) { + same_as> auto i = r.cend(); + if constexpr (common_range && sized_range && bidirectional_range) { + assert(*prev(i) == *prev(cend(expected))); + } + + if constexpr (CanCEnd) { + same_as> auto i2 = as_const(r).cend(); + if constexpr (common_range && sized_range && bidirectional_range) { + assert(*prev(i2) == *prev(cend(expected))); + } + } + } + + // Check view_interface::data + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanData); + + if (is_empty) { + return true; + } + + // Check view_interface::operator[] + STATIC_ASSERT(CanIndex == random_access_range); + if constexpr (CanIndex) { + assert(r[0] == expected[0]); + } + + // Check view_interface::operator[] (const) + STATIC_ASSERT(CanIndex == random_access_range); + if constexpr (CanIndex) { + assert(as_const(r)[0] == expected[0]); + } + + // Check view_interface::front + STATIC_ASSERT(CanMemberFront == forward_range); + if constexpr (CanMemberFront) { + assert(r.front() == *begin(expected)); + } + + // Check view_interface::front (const) + STATIC_ASSERT(CanMemberFront == forward_range); + if constexpr (CanMemberFront) { + assert(as_const(r).front() == *begin(expected)); + } + + // Check view_interface::back + STATIC_ASSERT(CanMemberBack == (bidirectional_range && common_range) ); + if constexpr (CanMemberBack) { + assert(r.back() == *prev(end(expected))); + } + + // Check view_interface::back (const) + STATIC_ASSERT(CanMemberBack == (bidirectional_range && common_range) ); + if constexpr (CanMemberBack) { + assert(as_const(r).back() == *prev(end(expected))); + } + + { // Check adjacent_view::iterator + using BI = iterator_t; + using I = iterator_t; + STATIC_ASSERT(forward_iterator); + + // Check iterator_category + STATIC_ASSERT(same_as); + + // Check iterator_concept + using IterConcept = typename I::iterator_concept; + STATIC_ASSERT(random_access_range == same_as); + STATIC_ASSERT( + (bidirectional_range && !random_access_range) == same_as); + STATIC_ASSERT((forward_range && !bidirectional_range) == same_as); + + // Check value_type + STATIC_ASSERT(same_as, N>>); + + // Check default-initializability + STATIC_ASSERT(default_initializable); + + auto i = r.begin(); + + { // Check dereference + same_as, N>> decltype(auto) v = *as_const(i); + assert(v == expected[0]); + } + + { // Check pre-incrementation + same_as decltype(auto) i2 = ++i; + assert(&i2 == &i); + if (i != r.end()) { +#pragma warning(suppress : 28020) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call + assert(*i == expected[1]); + } + i = r.begin(); + } + + { // Check post-incrementation + same_as decltype(auto) i2 = i++; + assert(*i2 == expected[0]); + if (i != r.end()) { +#pragma warning(suppress : 28020) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call + assert(*i == expected[1]); + } + i = r.begin(); + } + + { // Check equality comparisons + auto i2 = i; + same_as auto b1 = i == i2; + assert(b1); + ++i2; + same_as auto b2 = i != i2; + assert(b2); + } + + if constexpr (bidirectional_range) { + { // Check pre-decrementation + i = ranges::next(r.begin()); + + same_as decltype(auto) i2 = --i; + assert(&i2 == &i); + assert(*i2 == expected[0]); + } + + { // Check post-decrementation + i = ranges::next(r.begin()); + + same_as decltype(auto) i2 = i--; + if (i2 != r.end()) { +#pragma warning(suppress : 28020) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call + assert(*i2 == expected[1]); + } + assert(*i == expected[0]); + } + } + + if constexpr (random_access_range) { + { // Check advancement operators + same_as decltype(auto) i2 = (i += 1); + assert(&i2 == &i); + if (i != r.end()) { +#pragma warning(suppress : 28020) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call + assert(*i == expected[1]); + } + + same_as decltype(auto) i3 = (i -= 1); + assert(&i3 == &i); + assert(*i == expected[0]); + } + + { // Check subscript operator + same_as, N>> decltype(auto) v = i[0]; + assert(v == expected[0]); + } + + { // Check other comparisons + auto i2 = ranges::next(i); + same_as auto b1 = i < i2; + assert(b1); + same_as auto b2 = i2 > i; + assert(b2); + same_as auto b3 = i <= i2; + assert(b3); + same_as auto b4 = i2 >= i; + assert(b4); + } + + if constexpr (three_way_comparable) { // Check 3way comparisons + using Cat = compare_three_way_result_t; + auto i2 = i; + same_as auto cmp1 = i <=> i2; + assert(cmp1 == Cat::equivalent); + ++i2; + assert((i <=> i2) == Cat::less); + assert((i2 <=> i) == Cat::greater); + } + + { // Check operator+ + same_as auto i2 = i + 1; + if (i2 != r.end()) { +#pragma warning(suppress : 28020) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call + assert(*i2 == expected[1]); + } + + same_as auto i3 = 1 + i; + if (i3 != r.end()) { +#pragma warning(suppress : 28020) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call + assert(*i3 == expected[1]); + } + } + + { // Check operator-(Iter, Diff) + same_as auto i2 = ranges::next(i) - 1; + assert(*i2 == expected[0]); + } + } + + if constexpr (sized_sentinel_for) { // Check differencing + same_as> auto diff = i - i; + assert(diff == 0); + assert((i - ranges::next(i)) == -1); + assert((ranges::next(i) - i) == 1); + } + + if constexpr (sized_sentinel_for, I>) { // Check differencing with sentinel + const auto s = r.end(); + const auto size = ranges::ssize(expected); + const same_as> auto diff1 = i - s; + assert(diff1 == -size); + const same_as> auto diff2 = s - i; + assert(diff2 == size); + } + + if constexpr (sized_sentinel_for, I>) { // Check differencing with sentinel + const auto s = as_const(r).end(); + const auto size = ranges::ssize(expected); + const same_as> auto diff1 = i - s; + assert(diff1 == -size); + const same_as> auto diff2 = s - i; + assert(diff2 == size); + } + + { // Check iter_move + same_as, N>> decltype(auto) rval = iter_move(as_const(i)); + assert(rval == expected[0]); + STATIC_ASSERT(noexcept(iter_move(i)) + == (noexcept(ranges::iter_move(declval())) + && is_nothrow_move_constructible_v>) ); + } + + if constexpr (indirectly_swappable) { // Check iter_swap + STATIC_ASSERT(is_void_v); + STATIC_ASSERT( + noexcept(iter_swap(i, i)) == noexcept(ranges::iter_swap(declval(), declval()))); + // Note: other tests are defined in 'test_iter_swap' function + } + } + + // Check adjacent_view::iterator + if constexpr (CanMemberBegin) { + using CBI = iterator_t; + using CI = iterator_t; + STATIC_ASSERT(forward_iterator); + + // Check iterator_category + STATIC_ASSERT(same_as); + + // Check iterator_concept + using IterConcept = typename CI::iterator_concept; + STATIC_ASSERT(random_access_range == same_as); + STATIC_ASSERT((bidirectional_range && !random_access_range) + == same_as); + STATIC_ASSERT( + (forward_range && !bidirectional_range) == same_as); + + // Check value_type + STATIC_ASSERT(same_as, N>>); + + // Check default-initializability + STATIC_ASSERT(default_initializable); + + iterator_t i = r.begin(); + + // Check construction from non-const iterator + CI ci = i; + + { // Check dereference + same_as, N>> decltype(auto) v = *as_const(ci); + assert(v == expected[0]); + } + + { // Check pre-incrementation + same_as decltype(auto) ci2 = ++ci; + assert(&ci2 == &ci); + if (ci != r.end()) { +#pragma warning(suppress : 28020) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call + assert(*ci == expected[1]); + } + ci = r.begin(); + } + + { // Check post-incrementation + same_as decltype(auto) ci2 = ci++; + assert(*ci2 == expected[0]); + if (ci != r.end()) { +#pragma warning(suppress : 28020) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call + assert(*ci == expected[1]); + } + ci = r.begin(); + } + + { // Check equality comparisons + auto ci2 = ci; + same_as auto b1 = ci == ci2; + assert(b1); + ++ci2; + same_as auto b2 = ci != ci2; + assert(b2); + } + + { // Check equality comparisons with non-const iterators + same_as auto b1 = ci == i; + assert(b1); + ++i; + same_as auto b2 = ci != i; + assert(b2); + i = r.begin(); + } + + if constexpr (bidirectional_range) { + { // Check pre-decrementation + ci = ranges::next(r.begin()); + + same_as decltype(auto) ci2 = --ci; + assert(&ci2 == &ci); + assert(*ci2 == expected[0]); + } + + { // Check post-decrementation + ci = ranges::next(r.begin()); + + same_as decltype(auto) ci2 = ci--; + if (ci2 != r.end()) { +#pragma warning(suppress : 28020) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call + assert(*ci2 == expected[1]); + } + assert(*ci == expected[0]); + } + } + + if constexpr (random_access_range) { + { // Check advancement operators + same_as decltype(auto) ci2 = (ci += 1); + assert(&ci2 == &ci); + if (ci != r.end()) { +#pragma warning(suppress : 28020) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call + assert(*ci == expected[1]); + } + + same_as decltype(auto) ci3 = (ci -= 1); + assert(&ci3 == &ci); + assert(*ci == expected[0]); + } + + { // Check subscript operator + same_as, N>> decltype(auto) v = ci[0]; + assert(v == expected[0]); + } + + { // Check comparisons + auto ci2 = ranges::next(ci); + same_as auto b1 = ci < ci2; + assert(b1); + same_as auto b2 = ci2 > ci; + assert(b2); + same_as auto b3 = ci <= ci2; + assert(b3); + same_as auto b4 = ci2 >= ci; + assert(b4); + } + + { // Check comparisons with non-const iterators + ++i; + same_as auto b1 = ci < i; + assert(b1); + same_as auto b2 = i > ci; + assert(b2); + same_as auto b3 = ci <= i; + assert(b3); + same_as auto b4 = i >= ci; + assert(b4); + --i; + } + + if constexpr (three_way_comparable) { // Check 3way comparisons + using Cat = compare_three_way_result_t; + auto ci2 = ci; + same_as auto cmp1 = ci <=> ci2; + assert(cmp1 == Cat::equivalent); + ++ci2; + assert((ci <=> ci2) == Cat::less); + assert((ci2 <=> ci) == Cat::greater); + } + + if constexpr (three_way_comparable) { // Check 3way comparisons with non-const iterators + using Cat = compare_three_way_result_t; + same_as auto cmp1 = ci <=> i; + assert(cmp1 == Cat::equivalent); + ++i; + assert((ci <=> i) == Cat::less); + assert((i <=> ci) == Cat::greater); + --i; + } + + { // Check operator+ + same_as auto ci2 = ci + 1; + if (ci2 != r.end()) { +#pragma warning(suppress : 28020) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call + assert(*ci2 == expected[1]); + } + + same_as auto ci3 = 1 + ci; + if (ci3 != r.end()) { +#pragma warning(suppress : 28020) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call + assert(*ci3 == expected[1]); + } + } + + { // Check operator-(Iter, Diff) + same_as auto ci2 = ranges::next(ci) - 1; + assert(*ci2 == expected[0]); + } + } + + if constexpr (sized_sentinel_for) { // Check differencing + same_as> auto diff = ci - ci; + assert(diff == 0); + assert((ci - ranges::next(ci)) == -1); + assert((ranges::next(ci) - ci) == 1); + } + + if constexpr (sized_sentinel_for, CI>) { // Check differencing with sentinel + const auto s = r.end(); + const auto size = ranges::ssize(expected); + const same_as> auto diff1 = ci - s; + assert(diff1 == -size); + const same_as> auto diff2 = s - ci; + assert(diff2 == size); + } + + if constexpr (sized_sentinel_for, CI>) { // Check differencing with sentinel + const auto s = as_const(r).end(); + const auto size = ranges::ssize(expected); + const same_as> auto diff1 = ci - s; + assert(diff1 == -size); + const same_as> auto diff2 = s - ci; + assert(diff2 == size); + } + + { // Check iter_move + same_as, N>> decltype(auto) rval = iter_move(as_const(ci)); + assert(rval == expected[0]); + STATIC_ASSERT(noexcept(iter_move(ci)) + == (noexcept(ranges::iter_move(declval())) + && is_nothrow_move_constructible_v>) ); + } + + if constexpr (indirectly_swappable) { // Check iter_swap + STATIC_ASSERT(is_void_v); + STATIC_ASSERT(noexcept(iter_swap(ci, ci)) + == noexcept(ranges::iter_swap(declval(), declval()))); + // Note: other tests are defined in 'test_iter_swap' function + } + } + + // Check adjacent_view::base() const& + STATIC_ASSERT(CanMemberBase == copy_constructible); + if constexpr (copy_constructible) { + [[maybe_unused]] same_as auto b1 = as_const(r).base(); + STATIC_ASSERT(noexcept(as_const(r).base()) == is_nothrow_copy_constructible_v); // strengthened + } + + // Check adjacent_view::base() && + [[maybe_unused]] same_as auto b2 = move(r).base(); + STATIC_ASSERT(noexcept(move(r).base()) == is_nothrow_move_constructible_v); // strengthened + + return true; +} + +template +constexpr void test_adjacent0(Rng&& rng) { + STATIC_ASSERT(!CanConstructAdjacentView); + using V = views::all_t; + using E = ranges::empty_view>; + + // Check range closure object + constexpr auto closure = views::adjacent<0>; + constexpr bool is_view = ranges::view>; + + // ... with lvalue argument + STATIC_ASSERT(CanViewAdjacent == (!is_view || copy_constructible) ); + if constexpr (CanViewAdjacent) { + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(closure(rng))); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(rng | closure)); + } + + // ... with const lvalue argument + STATIC_ASSERT(CanViewAdjacent&, 0> == (!is_view || copy_constructible) ); + if constexpr (CanViewAdjacent&, 0>) { + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(closure(as_const(rng)))); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(rng) | closure)); + } + + // ... with rvalue argument + STATIC_ASSERT(CanViewAdjacent, 0> == (is_view || movable>) ); + if constexpr (CanViewAdjacent, 0>) { + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(closure(move(rng)))); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(rng) | closure)); + } + + // ... with const rvalue argument + STATIC_ASSERT(CanViewAdjacent, 0> == (is_view && copy_constructible) ); + if constexpr (CanViewAdjacent, 0>) { + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(closure(move(as_const(rng))))); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(as_const(rng)) | closure)); + } +} + +template + requires indirectly_swappable> +constexpr void test_iter_swap(Rng& rng) { + // This test assumes that 'ranges::size(views::adjacent(rng))' is at least 2 + auto r = views::adjacent(rng); + + { // Check iter_swap for adjacent_view::iterator + auto i = r.begin(); + auto first = *i; + auto j = ranges::next(i); + auto second = *j; + + // It takes N+1 swaps to get to initial state + for ([[maybe_unused]] size_t _ : views::iota(0u, N)) { + iter_swap(as_const(i), as_const(j)); + } + ranges::iter_swap(as_const(i), as_const(j)); + + assert(*i == first); + assert(*j == second); + } + + // Check iter_swap for adjacent_view::iterator + if constexpr (CanMemberBegin && indirectly_swappable>) { + auto i = as_const(r).begin(); + auto first = *i; + auto j = ranges::next(i); + auto second = *j; + + // It takes N+1 swaps to get to initial state + for ([[maybe_unused]] size_t _ : views::iota(0u, N)) { + iter_swap(as_const(i), as_const(j)); + } + ranges::iter_swap(as_const(i), as_const(j)); + + assert(*i == first); + assert(*j == second); + } +} + +// We have to use std::array instead of C-array, see LLVM-61025 +constexpr auto some_ints = array{1, 3, 5, 7, 11, 13}; +constexpr auto adjacent1_result = to_array>({{1}, {3}, {5}, {7}, {11}, {13}}); +constexpr auto pairwise_result = to_array>({{1, 3}, {3, 5}, {5, 7}, {7, 11}, {11, 13}}); +constexpr auto adjacent3_result = to_array>({{1, 3, 5}, {3, 5, 7}, {5, 7, 11}, {7, 11, 13}}); +constexpr auto adjacent4_result = to_array>({{1, 3, 5, 7}, {3, 5, 7, 11}, {5, 7, 11, 13}}); +constexpr auto adjacent5_result = to_array>({{1, 3, 5, 7, 11}, {3, 5, 7, 11, 13}}); +constexpr auto adjacent6_result = to_array>({{1, 3, 5, 7, 11, 13}}); +constexpr array, 0> adjacent7_result; + +template +using test_range = + test::range}, + IsCommon, test::CanCompare{derived_from || IsCommon == test::Common::yes}, + test::ProxyRef{!derived_from}>; + +struct instantiator { +#ifdef TEST_EVERYTHING + template + static constexpr void call() { + R r{some_ints}; + test_one<1>(r, adjacent1_result); + test_one<2>(r, pairwise_result); + test_one<4>(r, adjacent4_result); + test_one<7>(r, adjacent7_result); + test_adjacent0(r); + + // We cannot invoke 'test_iter_swap' in 'TEST_EVERYTHING' mode. + } +#else // ^^^ test all input range permutations / test only "interesting" permutations vvv + template + static constexpr void call() { + test_range r{some_ints}; + test_one<1>(r, adjacent1_result); + test_one<2>(r, pairwise_result); + test_one<4>(r, adjacent4_result); + test_one<7>(r, adjacent7_result); + test_adjacent0(r); + + int swap_check[4] = {1, 2, 3, 4}; + test_range swap_check_r{swap_check}; + test_iter_swap<2>(swap_check_r); + test_iter_swap<3>(swap_check_r); + } +#endif // TEST_EVERYTHING +}; + +constexpr void instantiation_test() { +#ifdef TEST_EVERYTHING + test_fwd(); +#else // ^^^ test all forward range permutations / test only "interesting" permutations vvv + using test::Common, test::Sized; + + // The view is sensitive to category, commonality, and size, but oblivious to proxyness and differencing + instantiator::call(); + instantiator::call(); + instantiator::call(); + instantiator::call(); + + instantiator::call(); + instantiator::call(); + instantiator::call(); + instantiator::call(); + + instantiator::call(); + instantiator::call(); + instantiator::call(); + instantiator::call(); + + instantiator::call(); + instantiator::call(); + instantiator::call(); + instantiator::call(); +#endif // TEST_EVERYTHING +} + +template > +using move_only_view = test::range}, + test::CanView::yes, test::Copyability::move_only>; + +int main() { + { // Check views + // ... copyable + constexpr span s{some_ints}; + STATIC_ASSERT(test_one<4>(s, adjacent4_result)); + test_one<4>(s, adjacent4_result); + } + + { // ... move-only + test_one<1>(move_only_view{some_ints}, adjacent1_result); + test_one<2>(move_only_view{some_ints}, pairwise_result); + test_one<3>(move_only_view{some_ints}, adjacent3_result); + test_one<4>(move_only_view{some_ints}, adjacent4_result); + test_one<5>(move_only_view{some_ints}, adjacent5_result); + test_one<6>(move_only_view{some_ints}, adjacent6_result); + } + + { // Check non-views + STATIC_ASSERT(test_one<2>(some_ints, pairwise_result)); + test_one<2>(some_ints, pairwise_result); + + auto vec = some_ints | ranges::to(); + test_one<3>(vec, adjacent3_result); + + auto lst = some_ints | ranges::to(); + test_one<4>(lst, adjacent4_result); + } + + { // Check empty range + STATIC_ASSERT(test_one<3>(span{}, span>{})); + test_one<3>(span{}, span>{}); + } + + STATIC_ASSERT((instantiation_test(), true)); + instantiation_test(); +} diff --git a/tests/std/tests/P2441R2_views_join_with/test.cpp b/tests/std/tests/P2441R2_views_join_with/test.cpp index 852d78745ce..a1e61bf65a0 100644 --- a/tests/std/tests/P2441R2_views_join_with/test.cpp +++ b/tests/std/tests/P2441R2_views_join_with/test.cpp @@ -187,7 +187,8 @@ constexpr void test_one(Outer&& rng, Delimiter&& delimiter, Expected&& expected) // Validate join_with_view::begin STATIC_ASSERT(CanMemberBegin); STATIC_ASSERT(CanMemberBegin - == (input_range && forward_range && is_reference_v>) ); + == (forward_range && forward_range && is_reference_v> + && input_range>) ); if (forward_range) { // intentionally not if constexpr const auto i = r.begin(); if (!is_empty) { @@ -222,7 +223,8 @@ constexpr void test_one(Outer&& rng, Delimiter&& delimiter, Expected&& expected) // Validate join_with_view::end static_assert(CanMemberEnd); static_assert(CanMemberEnd - == (input_range && forward_range && is_reference_v>) ); + == (forward_range && forward_range && is_reference_v> + && input_range>) ); static_assert(common_range == (forward_range && is_reference_v> && common_range && forward_range && common_range) ); @@ -570,7 +572,7 @@ struct FakeStr { }; void test_gh_3014() { // COMPILE-ONLY - struct InRange { + struct FwdRange { FakeStr* begin() { return nullptr; } @@ -588,11 +590,81 @@ void test_gh_3014() { // COMPILE-ONLY } }; - auto r = InRange{} | views::join_with('-'); + auto r = FwdRange{} | views::join_with('-'); [[maybe_unused]] decltype(as_const(r).begin()) i = r.begin(); // Check 'iterator(iterator i)' [[maybe_unused]] decltype(as_const(r).end()) s = r.end(); // Check 'sentinel(sentinel s)' } +constexpr bool test_lwg3698() { + // LWG-3698 "regex_iterator and join_view don't work together very well" + struct stashing_iterator { + using difference_type = int; + using value_type = span; + + int x = 1; + + constexpr stashing_iterator& operator++() { + ++x; + return *this; + } + constexpr void operator++(int) { + ++x; + } + constexpr value_type operator*() const { + return {&x, &x + 1}; + } + constexpr bool operator==(default_sentinel_t) const { + return x > 3; + } + }; + + auto r = ranges::subrange{stashing_iterator{}, default_sentinel} | views::join_with(views::empty); + auto r2 = r; + auto it = r.begin(); + auto it2 = r2.begin(); + + auto itcopy = it; + it = ++it2; + assert(*itcopy == 1); + + constexpr int expected_ints[] = {1, 2, 3, 5, 7}; + span intervals[2] = {{expected_ints + 0, expected_ints + 3}, {expected_ints + 3, expected_ints + 5}}; + + struct intricate_range { + span* p; + + constexpr stashing_iterator begin() { + return {}; + } + constexpr default_sentinel_t end() { + return {}; + } + constexpr const span* begin() const { + return p; + } + constexpr const span* end() const { + return p + 2; + } + }; + + auto jwv = intricate_range{.p = intervals} | views::join_with(views::empty); + auto cit = as_const(jwv).begin(); + assert(*++cit == 2); + assert(*--cit == 1); + assert(ranges::equal(as_const(jwv), expected_ints)); + + return true; +} + +void test_lwg3700() { // COMPILE-ONLY + // LWG-3700 "The const begin of the join_view family does not require InnerRng to be a range" + auto r = views::iota(0, 5) | views::filter([](auto) { return true; }); + auto j = views::single(r) | views::join_with(-1); + using J = decltype(j); + STATIC_ASSERT(!CanMemberBegin); + STATIC_ASSERT(!CanMemberEnd); +} + int main() { { auto filtered_and_joined = @@ -604,4 +676,7 @@ int main() { instantiation_test(); test_valueless_iterator(); + + STATIC_ASSERT(test_lwg3698()); + assert(test_lwg3698()); } diff --git a/tests/std/tests/P2442R1_views_chunk/env.lst b/tests/std/tests/P2442R1_views_chunk/env.lst index 18e2d7c71ec..8ac7033b206 100644 --- a/tests/std/tests/P2442R1_views_chunk/env.lst +++ b/tests/std/tests/P2442R1_views_chunk/env.lst @@ -1,4 +1,4 @@ # Copyright (c) Microsoft Corporation. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -RUNALL_INCLUDE ..\concepts_latest_matrix.lst +RUNALL_INCLUDE ..\strict_concepts_latest_matrix.lst diff --git a/tests/std/tests/P2442R1_views_chunk/test.cpp b/tests/std/tests/P2442R1_views_chunk/test.cpp index 685ac77ac66..be07d71a871 100644 --- a/tests/std/tests/P2442R1_views_chunk/test.cpp +++ b/tests/std/tests/P2442R1_views_chunk/test.cpp @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include #include #include @@ -464,8 +466,9 @@ template constexpr bool test_input(Rng&& rng, Expected&& expected) { using ranges::chunk_view, ranges::equal, ranges::iterator_t, ranges::sentinel_t; - using V = views::all_t; - using R = chunk_view; + using V = views::all_t; + using BI = iterator_t; + using R = chunk_view; same_as auto r = chunk_view{forward(rng), 2}; auto outer_iter = r.begin(); @@ -478,6 +481,19 @@ constexpr bool test_input(Rng&& rng, Expected&& expected) { auto inner_iter = val_ty.begin(); same_as auto inner_sen = val_ty.end(); + + { // Check iter_move (other tests are defined in 'test_lwg3851' function) + same_as> decltype(auto) rval = iter_move(as_const(inner_iter)); + assert(rval == expected[0][0]); + STATIC_ASSERT(noexcept(iter_move(inner_iter)) == noexcept(ranges::iter_move(declval()))); + } + + if constexpr (indirectly_swappable) { // Check iter_swap (other tests are defined in 'test_lwg3851' function) + STATIC_ASSERT(is_void_v); + STATIC_ASSERT(noexcept(iter_swap(inner_iter, inner_iter)) + == noexcept(ranges::iter_swap(declval(), declval()))); + } + assert(inner_iter != inner_sen); if constexpr (sized_sentinel_for, iterator_t>) { assert(inner_sen - inner_iter == 2); @@ -579,6 +595,38 @@ using move_only_view = test::range}, test::ProxyRef{!derived_from}, test::CanView::yes, test::Copyability::move_only>; +// Check LWG-3851: 'chunk_view::inner-iterator missing custom iter_move and iter_swap' +void test_lwg3851() { + { // Check 'iter_move' + istringstream ints{"0 1 2 3 4"}; + auto v = views::istream(ints) | views::chunk(2); + auto o = v.begin(); + auto c = *o; + auto i = c.begin(); + + same_as decltype(auto) rval = iter_move(i); + assert(rval == 0); + } + + { // Check 'iter_swap' + istringstream ints1{"0 1 2 3 4"}; + auto v1 = views::istream(ints1) | views::chunk(2); + auto o1 = v1.begin(); + auto c1 = *o1; + auto i1 = c1.begin(); + + istringstream ints2{"5 6 7 8 9"}; + auto v2 = views::istream(ints2) | views::chunk(2); + auto o2 = v2.begin(); + auto c2 = *o2; + auto i2 = c2.begin(); + + iter_swap(as_const(i1), as_const(i2)); + assert(*i1 == 5); + assert(*i2 == 0); + } +} + int main() { { // Validate views // ... copyable @@ -617,4 +665,6 @@ int main() { STATIC_ASSERT((instantiation_test(), true)); instantiation_test(); + + test_lwg3851(); } diff --git a/tests/std/tests/P2442R1_views_chunk_death/env.lst b/tests/std/tests/P2442R1_views_chunk_death/env.lst index 18e2d7c71ec..8ac7033b206 100644 --- a/tests/std/tests/P2442R1_views_chunk_death/env.lst +++ b/tests/std/tests/P2442R1_views_chunk_death/env.lst @@ -1,4 +1,4 @@ # Copyright (c) Microsoft Corporation. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -RUNALL_INCLUDE ..\concepts_latest_matrix.lst +RUNALL_INCLUDE ..\strict_concepts_latest_matrix.lst diff --git a/tests/std/tests/P2505R5_monadic_functions_for_std_expected/test.cpp b/tests/std/tests/P2505R5_monadic_functions_for_std_expected/test.cpp index 9ce4e74d8ae..39b6ffcc483 100644 --- a/tests/std/tests/P2505R5_monadic_functions_for_std_expected/test.cpp +++ b/tests/std/tests/P2505R5_monadic_functions_for_std_expected/test.cpp @@ -375,7 +375,153 @@ constexpr bool test() { return true; } +template class Tmpl> +constexpr bool is_specialization_of = false; + +template