diff --git a/stl/inc/ranges b/stl/inc/ranges index 5e820d13bf..7e08de8f4d 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -2217,9 +2217,9 @@ namespace ranges { is_nothrow_move_constructible_v>) // strengthened : _Current(_STD move(_Current_)), _Parent{_STD addressof(_Parent_)} { #if _ITERATOR_DEBUG_LEVEL != 0 - _Adl_verify_range(_Current, _RANGES end(_Parent_._Range)); + _STD _Adl_verify_range(_Current, _RANGES end(_Parent_._Range)); if constexpr (forward_range<_Vw>) { - _Adl_verify_range(_RANGES begin(_Parent_._Range), _Current); + _STD _Adl_verify_range(_RANGES begin(_Parent_._Range), _Current); } #endif // _ITERATOR_DEBUG_LEVEL != 0 } @@ -2324,7 +2324,7 @@ namespace ranges { } _NODISCARD constexpr bool _Equal(const sentinel_t<_Vw>& _Last) const - noexcept(noexcept(_Fake_copy_init(_Current == _Last))) { + noexcept(noexcept(_STD _Fake_copy_init(_Current == _Last))) { return _Current == _Last; } }; @@ -2494,9 +2494,9 @@ namespace ranges { is_nothrow_move_constructible_v>) // strengthened : _Current{_STD move(_Current_)}, _Parent{_STD addressof(_Parent_)} { #if _ITERATOR_DEBUG_LEVEL != 0 - _Adl_verify_range(_Current, _RANGES end(_Parent_._Range)); + _STD _Adl_verify_range(_Current, _RANGES end(_Parent_._Range)); if constexpr (forward_range<_Base>) { - _Adl_verify_range(_RANGES begin(_Parent_._Range), _Current); + _STD _Adl_verify_range(_RANGES begin(_Parent_._Range), _Current); } #endif // _ITERATOR_DEBUG_LEVEL != 0 } @@ -3752,8 +3752,8 @@ namespace ranges { 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); + _STD _Adl_verify_range(this->_Outer, _RANGES end(_Parent_._Range)); + _STD _Adl_verify_range(_RANGES begin(_Parent_._Range), this->_Outer); #endif // _ITERATOR_DEBUG_LEVEL != 0 _Satisfy(); } @@ -3809,7 +3809,7 @@ namespace ranges { _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(_As_lvalue(*_Get_outer())); + _Last = _RANGES end(_RANGES _As_lvalue(*_Get_outer())); } else { _Last = _RANGES end((*_Parent->_Inner)._Val); } @@ -3858,7 +3858,7 @@ namespace ranges { constexpr _Iterator& operator++() { auto& _Outer = _Get_outer(); if constexpr (_Deref_is_glvalue) { - if (++*_Inner == _RANGES end(_As_lvalue(*_Outer))) { + if (++*_Inner == _RANGES end(_RANGES _As_lvalue(*_Outer))) { ++_Outer; _Satisfy(); } @@ -3888,11 +3888,11 @@ namespace ranges { auto& _Outer = _Get_outer(); if (_Outer == _RANGES end(_Parent->_Range)) { --_Outer; - _Inner = _RANGES end(_As_lvalue(*_Outer)); + _Inner = _RANGES end(_RANGES _As_lvalue(*_Outer)); } - while (*_Inner == _RANGES begin(_As_lvalue(*_Outer))) { + while (*_Inner == _RANGES begin(_RANGES _As_lvalue(*_Outer))) { --_Outer; - *_Inner = _RANGES end(_As_lvalue(*_Outer)); + *_Inner = _RANGES end(_RANGES _As_lvalue(*_Outer)); } --*_Inner; return *this; @@ -3954,7 +3954,7 @@ 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._Get_outer() == _Last))) { + noexcept(noexcept(_STD _Fake_copy_init(_It._Get_outer() == _Last))) { return _It._Get_outer() == _Last; } @@ -4221,7 +4221,7 @@ namespace ranges { _NODISCARD constexpr auto& _Update_inner() { if constexpr (_Deref_is_glvalue) { - return _As_lvalue(*_Get_outer()); + return _RANGES _As_lvalue(*_Get_outer()); } else { return _Parent->_Inner._Emplace(_Construct_tag{}, _Get_outer())._Val; } @@ -4229,7 +4229,7 @@ namespace ranges { _NODISCARD constexpr auto& _Get_inner() noexcept { if constexpr (_Deref_is_glvalue) { - return _As_lvalue(*_Get_outer()); + return _RANGES _As_lvalue(*_Get_outer()); } else { return (*_Parent->_Inner)._Val; } @@ -4467,7 +4467,7 @@ namespace ranges { template _NODISCARD constexpr bool _Equal(const _Iterator<_OtherConst>& _It) const - noexcept(noexcept(_Fake_copy_init(_It._Get_outer() == _Last))) { + noexcept(noexcept(_STD _Fake_copy_init(_It._Get_outer() == _Last))) { _STL_INTERNAL_STATIC_ASSERT( sentinel_for, iterator_t<_Maybe_const<_OtherConst, _Vw>>>); return _It._Get_outer() == _Last; @@ -4693,7 +4693,7 @@ namespace ranges { } _NODISCARD constexpr bool _At_end() const - noexcept(noexcept(_Fake_copy_init(_Get_current() == _RANGES end(_Parent->_Range)))) { + noexcept(noexcept(_STD _Fake_copy_init(_Get_current() == _RANGES end(_Parent->_Range)))) { return _Get_current() == _RANGES end(_Parent->_Range); } @@ -5113,7 +5113,7 @@ namespace ranges { /* [[no_unique_address]] */ sentinel_t<_Vw> _Last{}; _NODISCARD constexpr bool _Equal(const _Iterator& _It) const - noexcept(noexcept(_Fake_copy_init(_It._Current == _Last))) { + noexcept(noexcept(_STD _Fake_copy_init(_It._Current == _Last))) { return !_It._Trailing_empty && _It._Current == _Last; } @@ -6306,7 +6306,7 @@ namespace ranges { 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 == _End))) { + noexcept(noexcept(_STD _Fake_copy_init(_It._Current == _End))) { return _It._Current == _End; } @@ -6850,38 +6850,38 @@ namespace ranges { } _NODISCARD_FRIEND constexpr bool operator==(const _Iterator& _Left, const _Iterator& _Right) noexcept( - noexcept(_Fake_copy_init(_Left._Current == _Right._Current))) /* strengthened */ { + noexcept(_STD _Fake_copy_init(_Left._Current == _Right._Current))) /* strengthened */ { return _Left._Current == _Right._Current; } _NODISCARD_FRIEND constexpr bool operator==(const _Iterator& _Left, default_sentinel_t) noexcept( - noexcept(_Fake_copy_init(_Left._Current == _Left._End))) /* strengthened */ { + noexcept(_STD _Fake_copy_init(_Left._Current == _Left._End))) /* strengthened */ { return _Left._Current == _Left._End; } _NODISCARD_FRIEND constexpr bool operator<(const _Iterator& _Left, const _Iterator& _Right) noexcept( - noexcept(_Fake_copy_init(_Left._Current < _Right._Current))) /* strengthened */ + noexcept(_STD _Fake_copy_init(_Left._Current < _Right._Current))) /* strengthened */ requires random_access_range<_Base> { return _Left._Current < _Right._Current; } _NODISCARD_FRIEND constexpr bool operator>(const _Iterator& _Left, const _Iterator& _Right) noexcept( - noexcept(_Fake_copy_init(_Right._Current < _Left._Current))) /* strengthened */ + noexcept(_STD _Fake_copy_init(_Right._Current < _Left._Current))) /* strengthened */ requires random_access_range<_Base> { return _Right._Current < _Left._Current; } _NODISCARD_FRIEND constexpr bool operator<=(const _Iterator& _Left, const _Iterator& _Right) noexcept( - noexcept(_Fake_copy_init(!(_Right._Current < _Left._Current)))) /* strengthened */ + noexcept(_STD _Fake_copy_init(!(_Right._Current < _Left._Current)))) /* strengthened */ requires random_access_range<_Base> { return !(_Right._Current < _Left._Current); } _NODISCARD_FRIEND constexpr bool operator>=(const _Iterator& _Left, const _Iterator& _Right) noexcept( - noexcept(_Fake_copy_init(!(_Left._Current < _Right._Current)))) /* strengthened */ + noexcept(_STD _Fake_copy_init(!(_Left._Current < _Right._Current)))) /* strengthened */ requires random_access_range<_Base> { return !(_Left._Current < _Right._Current); @@ -7209,7 +7209,7 @@ namespace ranges { } _NODISCARD_FRIEND constexpr bool operator==(const _Iterator& _Left, const _Iterator& _Right) noexcept( - noexcept(_Fake_copy_init(_Left._Current == _Right._Current))) /* strengthened */ { + noexcept(_STD _Fake_copy_init(_Left._Current == _Right._Current))) /* strengthened */ { if constexpr (_Slide_caches_first<_Base>) { return _Left._Last_element == _Right._Last_element; } else { @@ -7218,28 +7218,28 @@ namespace ranges { } _NODISCARD_FRIEND constexpr bool operator<(const _Iterator& _Left, const _Iterator& _Right) noexcept( - noexcept(_Fake_copy_init(_Left._Current < _Right._Current))) /* strengthened */ + noexcept(_STD _Fake_copy_init(_Left._Current < _Right._Current))) /* strengthened */ requires random_access_range<_Base> { return _Left._Current < _Right._Current; } _NODISCARD_FRIEND constexpr bool operator>(const _Iterator& _Left, const _Iterator& _Right) noexcept( - noexcept(_Fake_copy_init(_Right._Current < _Left._Current))) /* strengthened */ + noexcept(_STD _Fake_copy_init(_Right._Current < _Left._Current))) /* strengthened */ requires random_access_range<_Base> { return _Right._Current < _Left._Current; } _NODISCARD_FRIEND constexpr bool operator<=(const _Iterator& _Left, const _Iterator& _Right) noexcept( - noexcept(_Fake_copy_init(!(_Right._Current < _Left._Current)))) /* strengthened */ + noexcept(_STD _Fake_copy_init(!(_Right._Current < _Left._Current)))) /* strengthened */ requires random_access_range<_Base> { return !(_Right._Current < _Left._Current); } _NODISCARD_FRIEND constexpr bool operator>=(const _Iterator& _Left, const _Iterator& _Right) noexcept( - noexcept(_Fake_copy_init(!(_Left._Current < _Right._Current)))) /* strengthened */ + noexcept(_STD _Fake_copy_init(!(_Left._Current < _Right._Current)))) /* strengthened */ requires random_access_range<_Base> { return !(_Left._Current < _Right._Current); @@ -7304,8 +7304,8 @@ namespace ranges { _Sentinel() = default; _NODISCARD_FRIEND constexpr bool - operator==(const _Iterator& _Left, const _Sentinel& _Right) noexcept( - noexcept(_Fake_copy_init(_Left._Get_last_element() == _Right._Last))) /* strengthened */ { + operator==(const _Iterator& _Left, const _Sentinel& _Right) noexcept(noexcept( + _STD _Fake_copy_init(_Left._Get_last_element() == _Right._Last))) /* strengthened */ { return _Left._Get_last_element() == _Right._Last; } @@ -7557,12 +7557,12 @@ namespace ranges { } _NODISCARD_FRIEND constexpr bool operator==(const _Iterator& _Left, const _Iterator& _Right) noexcept( - noexcept(_Fake_copy_init(_Left._Current == _Right._Current))) /* strengthened */ { + noexcept(_STD _Fake_copy_init(_Left._Current == _Right._Current))) /* strengthened */ { return _Left._Current == _Right._Current; } _NODISCARD_FRIEND constexpr bool operator==(const _Iterator& _Left, default_sentinel_t) noexcept( - noexcept(_Fake_copy_init(_Left._Current == _Left._Next))) /* strengthened */ { + noexcept(_STD _Fake_copy_init(_Left._Current == _Left._Next))) /* strengthened */ { return _Left._Current == _Left._Next; } }; @@ -7824,40 +7824,40 @@ namespace ranges { } _NODISCARD_FRIEND constexpr bool operator==(const _Iterator& _It, default_sentinel_t) noexcept( - noexcept(_Fake_copy_init(_It._Current == _It._End))) /* strengthened */ { + noexcept(_STD _Fake_copy_init(_It._Current == _It._End))) /* strengthened */ { return _It._Current == _It._End; } _NODISCARD_FRIEND constexpr bool operator==(const _Iterator& _Left, const _Iterator& _Right) noexcept( - noexcept(_Fake_copy_init(_Left._Current == _Right._Current))) // strengthened + noexcept(_STD _Fake_copy_init(_Left._Current == _Right._Current))) // strengthened requires equality_comparable<_Base_iterator> { return _Left._Current == _Right._Current; } _NODISCARD_FRIEND constexpr bool operator<(const _Iterator& _Left, const _Iterator& _Right) noexcept( - noexcept(_Fake_copy_init(_Left._Current < _Right._Current))) // strengthened + noexcept(_STD _Fake_copy_init(_Left._Current < _Right._Current))) // strengthened requires random_access_range<_Base> { return _Left._Current < _Right._Current; } _NODISCARD_FRIEND constexpr bool operator>(const _Iterator& _Left, const _Iterator& _Right) noexcept( - noexcept(_Fake_copy_init(_Right._Current < _Left._Current))) // strengthened + noexcept(_STD _Fake_copy_init(_Right._Current < _Left._Current))) // strengthened requires random_access_range<_Base> { return _Right._Current < _Left._Current; } _NODISCARD_FRIEND constexpr bool operator<=(const _Iterator& _Left, const _Iterator& _Right) noexcept( - noexcept(_Fake_copy_init(!(_Right._Current < _Left._Current)))) // strengthened + noexcept(_STD _Fake_copy_init(!(_Right._Current < _Left._Current)))) // strengthened requires random_access_range<_Base> { return !(_Right._Current < _Left._Current); } _NODISCARD_FRIEND constexpr bool operator>=(const _Iterator& _Left, const _Iterator& _Right) noexcept( - noexcept(_Fake_copy_init(!(_Left._Current < _Right._Current)))) // strengthened + noexcept(_STD _Fake_copy_init(!(_Left._Current < _Right._Current)))) // strengthened requires random_access_range<_Base> { return !(_Left._Current < _Right._Current); @@ -9126,12 +9126,13 @@ namespace ranges { } _NODISCARD_FRIEND constexpr bool operator==(const _Iterator& _Left, const _Iterator& _Right) noexcept( - noexcept(_Fake_copy_init(_Left._Current.back() == _Right._Current.back()))) /* strengthened */ { + noexcept(_STD _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 + noexcept(_STD _Fake_copy_init(_Left._Current.back() < _Right._Current.back()))) // strengthened requires random_access_range<_Base> { return _Left._Current.back() < _Right._Current.back(); @@ -9236,7 +9237,7 @@ namespace ranges { 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))) { + noexcept(noexcept(_STD _Fake_copy_init(_It._Current.back() == _End))) { return _It._Current.back() == _End; } diff --git a/tests/std/test.lst b/tests/std/test.lst index 23a3ac9001..5a7f22df0b 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -156,6 +156,7 @@ tests\Dev11_1150223_shared_mutex tests\Dev11_1158803_regex_thread_safety tests\Dev11_1180290_filesystem_error_code tests\GH_000140_adl_proof_comparison +tests\GH_000140_adl_proof_views tests\GH_000177_forbidden_aliasing tests\GH_000178_uniform_int tests\GH_000342_filebuf_close diff --git a/tests/std/tests/GH_000140_adl_proof_views/env.lst b/tests/std/tests/GH_000140_adl_proof_views/env.lst new file mode 100644 index 0000000000..7b6bcff483 --- /dev/null +++ b/tests/std/tests/GH_000140_adl_proof_views/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\strict_concepts_20_matrix.lst diff --git a/tests/std/tests/GH_000140_adl_proof_views/test.compile.pass.cpp b/tests/std/tests/GH_000140_adl_proof_views/test.compile.pass.cpp new file mode 100644 index 0000000000..624b6e42d3 --- /dev/null +++ b/tests/std/tests/GH_000140_adl_proof_views/test.compile.pass.cpp @@ -0,0 +1,524 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef _M_CEE // TRANSITION, VSO-1659496 +#include +#include +#include +#include +#include +#include + +using namespace std; + +template +struct holder { + T t; +}; + +struct incomplete; + +constexpr holder* placeholder = nullptr; +constexpr holder* const* placeholder_addr = &placeholder; + +template +struct validating_iterator_provider { + struct iterator; +}; + +template +concept validating_iterator_specialization = requires { + typename I::reference; +} && same_as>::iterator>; + +template +struct validating_iterator_provider::iterator { + using value_type = remove_cv_t; + using pointer = T*; + using reference = T&; + using difference_type = ptrdiff_t; + using iterator_category = random_access_iterator_tag; + using iterator_concept = contiguous_iterator_tag; + + constexpr T& operator*() const noexcept { + return *ptr_; + } + + constexpr T* operator->() const noexcept { + return ptr_; + } + + constexpr T& operator[](const ptrdiff_t n) const noexcept { + return ptr_[n]; + } + + constexpr iterator& operator++() noexcept { + ++ptr_; + return *this; + } + constexpr iterator operator++(int) noexcept { + auto old = *this; + ++*this; + return old; + } + + constexpr iterator& operator--() noexcept { + --ptr_; + return *this; + } + constexpr iterator operator--(int) noexcept { + auto old = *this; + --*this; + return old; + } + + constexpr iterator& operator+=(const ptrdiff_t n) noexcept { + ptr_ += n; + return *this; + } + + constexpr iterator& operator-=(const ptrdiff_t n) noexcept { + ptr_ -= n; + return *this; + } + + friend constexpr iterator operator+(const iterator i, const ptrdiff_t n) noexcept { + return {i.ptr_ + n}; + } + friend constexpr iterator operator+(const ptrdiff_t n, const iterator i) noexcept { + return {i.ptr_ + n}; + } + + friend constexpr ptrdiff_t operator-(const iterator i, const iterator j) noexcept { + return i.ptr_ - j.ptr_; + } + friend constexpr iterator operator-(const iterator i, const ptrdiff_t n) noexcept { + return iterator{i.ptr_ - n}; + } + + template + friend constexpr decltype(auto) operator==(iterator i, OtherIter j) noexcept + requires requires { pointer{} == typename OtherIter::pointer{} ? placeholder_addr : nullptr; } + { + return i.operator->() == j.operator->() ? placeholder_addr : nullptr; + } + template + friend constexpr decltype(auto) operator!=(iterator i, OtherIter j) noexcept + requires requires { pointer{} != typename OtherIter::pointer{} ? placeholder_addr : nullptr; } + { + return i.operator->() != j.operator->() ? placeholder_addr : nullptr; + } + + template + friend constexpr decltype(auto) operator<(iterator i, OtherIter j) noexcept + requires requires { pointer{} < typename OtherIter::pointer{} ? placeholder_addr : nullptr; } + { + return i.operator->() < j.operator->() ? placeholder_addr : nullptr; + } + template + friend constexpr decltype(auto) operator>(iterator i, OtherIter j) noexcept + requires requires { pointer{} > typename OtherIter::pointer{} ? placeholder_addr : nullptr; } + { + return i.operator->() > j.operator->() ? placeholder_addr : nullptr; + } + template + friend constexpr decltype(auto) operator<=(iterator i, OtherIter j) + requires requires { pointer{} <= typename OtherIter::pointer{} ? placeholder_addr : nullptr; } + { + return i.operator->() <= j.operator->() ? placeholder_addr : nullptr; + } + template + friend constexpr decltype(auto) operator>=(iterator i, OtherIter j) noexcept + requires requires { pointer{} >= typename OtherIter::pointer{} ? placeholder_addr : nullptr; } + { + return i.operator->() >= j.operator->() ? placeholder_addr : nullptr; + } + + template + requires (!is_same_v) && convertible_to + constexpr operator OtherIter() const noexcept { + return {ptr_}; + } + + T* ptr_; +}; + +struct validating_view_for_verification { + constexpr holder** begin() const noexcept { + return begin_; + } + + constexpr holder** end() const noexcept { + return end_; + } + + holder** begin_; + holder** end_; +}; + +template <> +inline constexpr bool ranges::enable_view = true; + +template <> +inline constexpr bool ranges::enable_borrowed_range = true; + +template +struct tagged_ints_view { + constexpr int* begin() const noexcept { + return begin_; + } + + constexpr int* end() const noexcept { + return end_; + } + + int* begin_; + int* end_; +}; + +template +inline constexpr bool ranges::enable_view> = true; + +template +inline constexpr bool ranges::enable_borrowed_range> = true; + +struct validating_view_to_join_for_verification { + constexpr const tagged_ints_view>* begin() const noexcept { + return begin_; + } + + constexpr const tagged_ints_view>* end() const noexcept { + return end_; + } + + const tagged_ints_view>* begin_; + const tagged_ints_view>* end_; +}; + +template <> +inline constexpr bool ranges::enable_view = true; + +template <> +inline constexpr bool ranges::enable_borrowed_range = true; + +void test_views_verification() { + constexpr auto simple_truth = [](const auto&) { return true; }; + + holder* varr[1]{}; + const validating_view_for_verification validating_view_val{varr, varr + 1}; + + for (auto&& _ [[maybe_unused]] : validating_view_val | views::filter(simple_truth)) { + } + + for (auto&& _ [[maybe_unused]] : validating_view_val | views::transform(simple_truth)) { + } + + int iarr[1]{}; + const tagged_ints_view> inner_arr[1]{{iarr, iarr + 1}}; + const validating_view_to_join_for_verification view_to_join{inner_arr, inner_arr + 1}; + + for (auto&& _ [[maybe_unused]] : view_to_join | views::join) { + } + + for (auto&& _ [[maybe_unused]] : view_to_join | views::join | views::reverse) { + } + +#if _HAS_CXX23 + const tagged_ints_view simple_pattern{iarr, iarr + 1}; + + for (auto&& _ [[maybe_unused]] : view_to_join | views::join_with(simple_pattern)) { + } +#endif // _HAS_CXX23 +} + +struct validating_common_view { + using iterator = validating_iterator_provider::iterator; + + constexpr iterator begin() const noexcept { + return {begin_}; + } + + constexpr iterator end() const noexcept { + return {end_}; + } + + int* begin_; + int* end_; +}; + +template <> +inline constexpr bool ranges::enable_view = true; + +template <> +inline constexpr bool ranges::enable_borrowed_range = true; + +template +struct noncommon_sentinel_for { + using pointer = iterator_traits::pointer; + + pointer p_end_; + + friend constexpr holder* const* operator==(const I i, const noncommon_sentinel_for s) noexcept { + return to_address(i) == s.p_end_ ? placeholder_addr : nullptr; + } + friend constexpr holder* const* operator==(const noncommon_sentinel_for s, const I i) noexcept { + return to_address(i) == s.p_end_ ? placeholder_addr : nullptr; + } + friend constexpr holder* const* operator!=(const I i, const noncommon_sentinel_for s) noexcept { + return to_address(i) == s.p_end_ ? nullptr : placeholder_addr; + } + friend constexpr holder* const* operator!=(const noncommon_sentinel_for s, const I i) noexcept { + return to_address(i) == s.p_end_ ? nullptr : placeholder_addr; + } +}; + +struct validating_noncommon_view { + using iterator = validating_iterator_provider::iterator; + + constexpr iterator begin() const noexcept { + return {begin_}; + } + + constexpr noncommon_sentinel_for end() const noexcept { + return {end_}; + } + + int* begin_; + int* end_; +}; + +template <> +inline constexpr bool ranges::enable_view = true; + +template <> +inline constexpr bool ranges::enable_borrowed_range = true; + + +struct validating_noncommon_to_join_view { + using iterator = const validating_noncommon_view*; + + constexpr iterator begin() const noexcept { + return begin_; + } + + constexpr noncommon_sentinel_for end() const noexcept { + return {end_}; + } + + const validating_noncommon_view* begin_; + const validating_noncommon_view* end_; +}; + +template <> +inline constexpr bool ranges::enable_view = true; + +template <> +inline constexpr bool ranges::enable_borrowed_range = true; + +struct negation_validating_iterator { + struct boolean_testable_result { + constexpr operator bool() const noexcept { + return val_; + } + + constexpr holder* const* operator!() const noexcept { + return val_ ? placeholder_addr : nullptr; + } + bool val_; + }; + + using value_type = int; + using reference = int&; + using pointer = int*; + using difference_type = ptrdiff_t; + using iterator_category = random_access_iterator_tag; + using iterator_concept = contiguous_iterator_tag; + + constexpr int& operator*() const noexcept { + return *ptr_; + } + + constexpr int* operator->() const noexcept { + return ptr_; + } + + constexpr negation_validating_iterator& operator++() noexcept { + ++ptr_; + return *this; + } + constexpr negation_validating_iterator operator++(int) noexcept { + auto old = *this; + ++ptr_; + return old; + } + + constexpr negation_validating_iterator& operator--() noexcept { + --ptr_; + return *this; + } + constexpr negation_validating_iterator operator--(int) noexcept { + auto old = *this; + --ptr_; + return old; + } + + constexpr negation_validating_iterator& operator+=(const ptrdiff_t n) noexcept { + ptr_ += n; + return *this; + } + + constexpr negation_validating_iterator& operator-=(const ptrdiff_t n) noexcept { + ptr_ -= n; + return *this; + } + + constexpr int& operator[](const ptrdiff_t n) const noexcept { + return ptr_[n]; + } + + friend constexpr negation_validating_iterator operator+( + const negation_validating_iterator i, const ptrdiff_t n) noexcept { + return {i.ptr_ + n}; + } + friend constexpr negation_validating_iterator operator+( + const ptrdiff_t n, const negation_validating_iterator i) noexcept { + return {i.ptr_ + n}; + } + friend constexpr negation_validating_iterator operator-( + const negation_validating_iterator i, const ptrdiff_t n) noexcept { + return {i.ptr_ - n}; + } + friend constexpr ptrdiff_t operator-( + const negation_validating_iterator i, const negation_validating_iterator j) noexcept { + return i.ptr_ - j.ptr_; + } + + friend constexpr boolean_testable_result operator==( + const negation_validating_iterator i, const negation_validating_iterator j) noexcept { + return {i.ptr_ == j.ptr_}; + } + + friend constexpr boolean_testable_result operator!=( + const negation_validating_iterator i, const negation_validating_iterator j) noexcept { + return {i.ptr_ != j.ptr_}; + } + + friend constexpr boolean_testable_result operator<( + const negation_validating_iterator i, const negation_validating_iterator j) noexcept { + return {i.ptr_ < j.ptr_}; + } + + friend constexpr boolean_testable_result operator>( + const negation_validating_iterator i, const negation_validating_iterator j) noexcept { + return {i.ptr_ > j.ptr_}; + } + + friend constexpr boolean_testable_result operator<=( + const negation_validating_iterator i, const negation_validating_iterator j) noexcept { + return {i.ptr_ <= j.ptr_}; + } + + friend constexpr boolean_testable_result operator>=( + const negation_validating_iterator i, const negation_validating_iterator j) noexcept { + return {i.ptr_ >= j.ptr_}; + } + + int* ptr_; +}; + +struct validating_negation_view { + constexpr negation_validating_iterator begin() const noexcept { + return {begin_}; + } + + constexpr negation_validating_iterator end() const noexcept { + return {end_}; + } + + int* begin_; + int* end_; +}; + +template <> +inline constexpr bool ranges::enable_view = true; + +template <> +inline constexpr bool ranges::enable_borrowed_range = true; + +template +void test_adl_proof_random_access_views_comparison(V&& v) { + auto i = v.begin(); + auto j = v.end(); + + (void) (i == j); + (void) (i != j); + (void) (i < j); + (void) (i > j); + (void) (i <= j); + (void) (i >= j); +} + +template +void test_adl_proof_basic_views_comparison(V&& v) { + auto it = v.begin(); + auto se = v.end(); + + (void) (it == se); + (void) (it != se); +} + +void test_adl_proof_views_comparison() { + constexpr auto simple_truth = [](const auto&) { return true; }; + + int iarr[1]{}; + const validating_common_view validating_common_view_val{iarr, iarr + 1}; + const validating_noncommon_view validating_noncommon_view_val{iarr, iarr + 1}; + const validating_negation_view validating_negation_view_val{iarr, iarr + 1}; + const validating_noncommon_to_join_view validating_view_to_join{ + &validating_noncommon_view_val, &validating_noncommon_view_val + 1}; + + test_adl_proof_basic_views_comparison(validating_common_view_val | views::filter(simple_truth)); + test_adl_proof_basic_views_comparison(validating_negation_view_val | views::filter(simple_truth)); + test_adl_proof_basic_views_comparison(validating_noncommon_view_val | views::filter(simple_truth)); + + test_adl_proof_basic_views_comparison(validating_view_to_join | views::join); + + test_adl_proof_basic_views_comparison(validating_common_view_val | views::split(validating_common_view_val)); + test_adl_proof_basic_views_comparison(validating_negation_view_val | views::split(validating_negation_view_val)); + test_adl_proof_basic_views_comparison(validating_noncommon_view_val | views::split(validating_noncommon_view_val)); + + test_adl_proof_basic_views_comparison(validating_common_view_val | views::lazy_split(validating_common_view_val)); + test_adl_proof_basic_views_comparison( + validating_negation_view_val | views::lazy_split(validating_negation_view_val)); + test_adl_proof_basic_views_comparison( + validating_noncommon_view_val | views::lazy_split(validating_noncommon_view_val)); + +#if _HAS_CXX23 + test_adl_proof_basic_views_comparison(validating_view_to_join | views::join_with(validating_noncommon_view_val)); + + test_adl_proof_random_access_views_comparison(validating_common_view_val | views::enumerate); + test_adl_proof_random_access_views_comparison(validating_negation_view_val | views::enumerate); + test_adl_proof_basic_views_comparison(validating_noncommon_view_val | views::enumerate); + + test_adl_proof_random_access_views_comparison(validating_common_view_val | views::chunk(1)); + test_adl_proof_random_access_views_comparison(validating_negation_view_val | views::chunk(1)); + test_adl_proof_basic_views_comparison(validating_noncommon_view_val | views::chunk(1)); + + test_adl_proof_random_access_views_comparison(validating_common_view_val | views::slide(1)); + test_adl_proof_random_access_views_comparison(validating_negation_view_val | views::slide(1)); + test_adl_proof_basic_views_comparison(validating_noncommon_view_val | views::slide(1)); + + test_adl_proof_basic_views_comparison(validating_common_view_val | views::chunk_by(ranges::equal_to{})); + test_adl_proof_basic_views_comparison(validating_negation_view_val | views::chunk_by(ranges::equal_to{})); + test_adl_proof_basic_views_comparison(validating_noncommon_view_val | views::chunk_by(ranges::equal_to{})); + + test_adl_proof_random_access_views_comparison(validating_common_view_val | views::stride(1)); + test_adl_proof_random_access_views_comparison(validating_negation_view_val | views::stride(1)); + test_adl_proof_basic_views_comparison(validating_noncommon_view_val | views::stride(1)); + + test_adl_proof_random_access_views_comparison(validating_common_view_val | views::adjacent<1>); + test_adl_proof_random_access_views_comparison(validating_negation_view_val | views::adjacent<1>); + test_adl_proof_basic_views_comparison(validating_noncommon_view_val | views::adjacent<1>); +#endif // _HAS_CXX23 +} +#endif // ^^^ no workaround ^^^