From 0ff884e13121e813abae29e0b87f9c8d1eed935f Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Fri, 19 Aug 2022 14:38:19 +0200 Subject: [PATCH 01/25] Implement `std::const_iterator` --- stl/inc/xutility | 331 ++++++++++++++++++ stl/inc/yvals_core.h | 2 + .../env.lst | 4 + .../test.compile.pass.cpp | 166 +++++++++ 4 files changed, 503 insertions(+) create mode 100644 tests/std/tests/P2278R4_ranges_const_iterator_machinery/env.lst create mode 100644 tests/std/tests/P2278R4_ranges_const_iterator_machinery/test.compile.pass.cpp diff --git a/stl/inc/xutility b/stl/inc/xutility index 413575eb86b..5d6d4a977af 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -1690,6 +1690,337 @@ _NODISCARD constexpr const _Elem* data(initializer_list<_Elem> _Ilist) noexcept template concept _Not_same_as = !same_as, remove_cvref_t<_Ty2>>; +#if _HAS_CXX23 +template +using iter_const_reference_t = common_reference_t&&, iter_reference_t<_Ty>>; + +template +concept _Constant_iterator = input_iterator<_Ty> && same_as, iter_reference_t<_Ty>>; + +template +class basic_const_iterator; + +template +using const_iterator = conditional_t<_Constant_iterator<_Iter>, _Iter, basic_const_iterator<_Iter>>; + +template +struct _Const_sentinel { + using type = _Sent; +}; + +template +struct _Const_sentinel<_Sent> { + using type = const_iterator<_Sent>; +}; + +template +using const_sentinel = typename _Const_sentinel<_Sent>::type; + +template +inline constexpr bool _Is_basic_const_iterator_v = false; + +template +inline constexpr bool _Is_basic_const_iterator_v> = true; + +template +concept _Not_a_const_iterator = !_Is_basic_const_iterator_v<_Ty>; + +template +struct _Basic_const_iterator_category {}; + +template +struct _Basic_const_iterator_category<_Iter> { + using iterator_category = typename iterator_traits<_Iter>::iterator_category; +}; + +template +class basic_const_iterator : public _Basic_const_iterator_category<_Iter> { +private: + /* [[no_unique_address]] */ _Iter _Current{}; + + using _Reference = iter_const_reference_t<_Iter>; + + _NODISCARD static _CONSTEVAL auto _Get_iter_concept() noexcept { + if constexpr (contiguous_iterator<_Iter>) { + return contiguous_iterator_tag{}; + } else if constexpr (random_access_iterator<_Iter>) { + return random_access_iterator_tag{}; + } else if constexpr (bidirectional_iterator<_Iter>) { + return bidirectional_iterator_tag{}; + } else if constexpr (forward_iterator<_Iter>) { + return forward_iterator_tag{}; + } else { + return input_iterator_tag{}; + } + } + +public: + using iterator_concept = decltype(_Get_iter_concept()); + using value_type = iter_value_t<_Iter>; + using difference_type = iter_difference_t<_Iter>; + + // clang-format off + basic_const_iterator() requires default_initializable<_Iter> = default; + // clang-format on + + constexpr basic_const_iterator(_Iter _Current_) noexcept(is_nothrow_move_constructible_v<_Iter>) // strengthened + : _Current(_STD move(_Current_)) {} + + template _Other> + constexpr basic_const_iterator(basic_const_iterator<_Other> _Current_) noexcept( + is_nothrow_constructible_v<_Iter, _Other>) // strengthened + : _Current(_STD move(_Current_._Current)) {} + + template <_Not_same_as _Other> + requires convertible_to<_Other, _Iter> + constexpr basic_const_iterator(_Other&& _Current_) noexcept( + is_nothrow_constructible_v<_Iter, _Other>) // strengthened + : _Current(_STD forward<_Other>(_Current_)) {} + + _NODISCARD constexpr const _Iter& base() const& noexcept { + return _Current; + } + + _NODISCARD constexpr _Iter base() && noexcept(is_nothrow_move_constructible_v<_Iter>) /* strengthened */ { + return _STD move(_Current); + } + + _NODISCARD constexpr _Reference operator*() const + noexcept(noexcept(static_cast<_Reference>(*_Current))) /* strengthened */ { + return static_cast<_Reference>(*_Current); + } + + _NODISCARD constexpr const value_type* operator->() const + noexcept(contiguous_iterator<_Iter> || noexcept(*_Current)) /* strengthened */ requires + is_lvalue_reference_v> && same_as>, value_type> { + if constexpr (contiguous_iterator<_Iter>) { + return _STD to_address(_Current); + } else { + return _STD addressof(*_Current); + } + } + + constexpr basic_const_iterator& operator++() noexcept(noexcept(++_Current)) /* strengthened */ { + ++_Current; + return *this; + } + + constexpr void operator++(int) noexcept(noexcept(++_Current)) /* strengthened */ { + ++_Current; + } + + constexpr basic_const_iterator operator++(int) noexcept( + noexcept(++*this) && is_nothrow_copy_constructible_v) // strengthened + requires forward_iterator<_Iter> { + auto _Tmp = *this; + ++*this; + return _Tmp; + } + + constexpr basic_const_iterator& operator--() noexcept(noexcept(--_Current)) // strengthened + requires bidirectional_iterator<_Iter> { + --_Current; + return *this; + } + + constexpr basic_const_iterator operator--(int) noexcept( + noexcept(--*this) && is_nothrow_copy_constructible_v) // strengthened + requires bidirectional_iterator<_Iter> { + auto _Tmp = *this; + --*this; + return _Tmp; + } + + constexpr basic_const_iterator& operator+=(const difference_type _Off) noexcept( + noexcept(_Current += _Off)) // strengthened + requires random_access_iterator<_Iter> { + _Current += _Off; + return *this; + } + + constexpr basic_const_iterator& operator-=(const difference_type _Off) noexcept( + noexcept(_Current -= _Off)) // strengthened + requires random_access_iterator<_Iter> { + _Current -= _Off; + return *this; + } + + _NODISCARD constexpr _Reference operator[](const difference_type _Idx) const + noexcept(noexcept(static_cast<_Reference>(_Current[_Idx]))) // strengthened + requires random_access_iterator<_Iter> { + return static_cast<_Reference>(_Current[_Idx]); + } + + template _Sent> + _NODISCARD_FRIEND constexpr bool operator==(const basic_const_iterator& _It, const _Sent& _Se) noexcept( + noexcept(_It._Current == _Se)) /* strengthened */ { + return _It._Current == _Se; + } + + _NODISCARD_FRIEND constexpr bool + operator<(const basic_const_iterator& _Left, const basic_const_iterator& _Right) noexcept( + noexcept(_Implicitly_convert_to(_Left._Current < _Right._Current))) // strengthened + requires random_access_iterator<_Iter> { + return _Left._Current < _Right._Current; + } + + _NODISCARD_FRIEND constexpr bool + operator>(const basic_const_iterator& _Left, const basic_const_iterator& _Right) noexcept( + noexcept(_Implicitly_convert_to(_Left._Current > _Right._Current))) // strengthened + requires random_access_iterator<_Iter> { + return _Left._Current > _Right._Current; + } + + _NODISCARD_FRIEND constexpr bool + operator<=(const basic_const_iterator& _Left, const basic_const_iterator& _Right) noexcept( + noexcept(_Implicitly_convert_to(_Left._Current <= _Right._Current))) // strengthened + requires random_access_iterator<_Iter> { + return _Left._Current <= _Right._Current; + } + + _NODISCARD_FRIEND constexpr bool + operator>=(const basic_const_iterator& _Left, const basic_const_iterator& _Right) noexcept( + noexcept(_Implicitly_convert_to(_Left._Current >= _Right._Current))) // strengthened + requires random_access_iterator<_Iter> { + return _Left._Current >= _Right._Current; + } + + _NODISCARD_FRIEND constexpr auto // + operator<=>(const basic_const_iterator& _Left, const basic_const_iterator& _Right) noexcept( + noexcept(_Left._Current <=> _Right._Current)) // strengthened + requires random_access_iterator<_Iter> && three_way_comparable<_Iter> { + return _Left._Current <=> _Right._Current; + } + + template <_Not_same_as _Other> + _NODISCARD_FRIEND constexpr bool operator<(const basic_const_iterator& _Left, const _Other& _Right) noexcept( + noexcept(_Implicitly_convert_to(_Left._Current < _Right))) // strengthened + requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { + return _Left._Current < _Right; + } + + template <_Not_same_as _Other> + _NODISCARD_FRIEND constexpr bool operator>(const basic_const_iterator& _Left, const _Other& _Right) noexcept( + noexcept(_Implicitly_convert_to(_Left._Current > _Right))) // strengthened + requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { + return _Left._Current > _Right; + } + + template <_Not_same_as _Other> + _NODISCARD_FRIEND constexpr bool operator<=(const basic_const_iterator& _Left, const _Other& _Right) noexcept( + noexcept(_Implicitly_convert_to(_Left._Current <= _Right))) // strengthened + requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { + return _Left._Current <= _Right; + } + + template <_Not_same_as _Other> + _NODISCARD_FRIEND constexpr bool operator>=(const basic_const_iterator& _Left, const _Other& _Right) noexcept( + noexcept(_Implicitly_convert_to(_Left._Current >= _Right))) // strengthened + requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { + return _Left._Current >= _Right; + } + + template <_Not_same_as _Other> + _NODISCARD_FRIEND constexpr auto operator<=>(const basic_const_iterator& _Left, const _Other& _Right) noexcept( + noexcept(_Left._Current <=> _Right)) // strengthened + requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> && // + three_way_comparable_with<_Iter, _Other> { + return _Left._Current <=> _Right; + } + + template <_Not_a_const_iterator _Other> + _NODISCARD_FRIEND constexpr bool operator<(const _Other& _Left, const basic_const_iterator& _Right) noexcept( + noexcept(_Implicitly_convert_to(_Left < _Right._Current))) // strengthened + requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { + return _Left < _Right._Current; + } + + template <_Not_a_const_iterator _Other> + _NODISCARD_FRIEND constexpr bool operator>(const _Other& _Left, const basic_const_iterator& _Right) noexcept( + noexcept(_Implicitly_convert_to(_Left > _Right._Current))) // strengthened + requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { + return _Left > _Right._Current; + } + + template <_Not_a_const_iterator _Other> + _NODISCARD_FRIEND constexpr bool operator<=(const _Other& _Left, const basic_const_iterator& _Right) noexcept( + noexcept(_Implicitly_convert_to(_Left <= _Right._Current))) // strengthened + requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { + return _Left <= _Right._Current; + } + + template <_Not_a_const_iterator _Other> + _NODISCARD_FRIEND constexpr bool operator>=(const _Other& _Left, const basic_const_iterator& _Right) noexcept( + noexcept(_Implicitly_convert_to(_Left >= _Right._Current))) // strengthened + requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { + return _Left >= _Right._Current; + } + + _NODISCARD_FRIEND constexpr basic_const_iterator operator+( + const basic_const_iterator& _It, const difference_type _Off) // + noexcept(noexcept(basic_const_iterator{_It._Current + _Off})) // strengthened + requires random_access_iterator<_Iter> { + return basic_const_iterator{_It._Current + _Off}; + } + + _NODISCARD_FRIEND constexpr basic_const_iterator operator+( + const difference_type _Off, const basic_const_iterator& _It) // + noexcept(noexcept(basic_const_iterator{_It._Current + _Off})) // strengthened + requires random_access_iterator<_Iter> { + return basic_const_iterator{_It._Current + _Off}; + } + + _NODISCARD_FRIEND constexpr basic_const_iterator operator-( + const basic_const_iterator& _It, const difference_type _Off) // + noexcept(noexcept(basic_const_iterator{_It._Current - _Off})) // strengthened + requires random_access_iterator<_Iter> { + return basic_const_iterator{_It._Current - _Off}; + } + + template _Sent> + _NODISCARD_FRIEND constexpr difference_type operator-(const basic_const_iterator& _It, const _Sent& _Se) noexcept( + noexcept(_It._Current - _Se)) /* strengthened */ { + return _It._Current - _Se; + } + + // clang-format off + template _Sent> + requires _Not_same_as<_Sent, basic_const_iterator> + _NODISCARD_FRIEND constexpr difference_type operator-(const _Sent& _Se, const basic_const_iterator& _It) + noexcept(noexcept(_Se - _It._Current)) /* strengthened */ { + // clang-format on + return _Se - _It._Current; + } +}; + +template _Ty2> +struct common_type, _Ty2> { + using type = basic_const_iterator>; +}; + +template _Ty2> +struct common_type<_Ty2, basic_const_iterator<_Ty1>> { + using type = basic_const_iterator>; +}; + +template _Ty2> +struct common_type, basic_const_iterator<_Ty2>> { + using type = basic_const_iterator>; +}; + +template +_NODISCARD constexpr const_iterator<_Iter> make_const_iterator(_Iter _It) noexcept( + is_nothrow_constructible_v, _Iter>) /* strengthened */ { + return _It; +} + +template +_NODISCARD constexpr const_sentinel<_Sent> make_const_sentinel(_Sent _Se) noexcept( + is_nothrow_constructible_v, _Sent>) /* strengthened */ { + return _Se; +} +#endif // _HAS_CXX23 + namespace ranges { template inline constexpr bool _Has_complete_elements = false; diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 68ad8fa7c49..3a578e4f129 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -321,6 +321,8 @@ // P2166R1 Prohibiting basic_string And basic_string_view Construction From nullptr // P2186R2 Removing Garbage Collection Support // P2273R3 constexpr unique_ptr +// P2278R4 cbegin Should Always Return A Constant Iterator +// ("Iterators" section from the paper only) // P2302R4 ranges::contains, ranges::contains_subrange // P2321R2 zip // (changes to pair, tuple, and vector::reference only) diff --git a/tests/std/tests/P2278R4_ranges_const_iterator_machinery/env.lst b/tests/std/tests/P2278R4_ranges_const_iterator_machinery/env.lst new file mode 100644 index 00000000000..18e2d7c71ec --- /dev/null +++ b/tests/std/tests/P2278R4_ranges_const_iterator_machinery/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_latest_matrix.lst diff --git a/tests/std/tests/P2278R4_ranges_const_iterator_machinery/test.compile.pass.cpp b/tests/std/tests/P2278R4_ranges_const_iterator_machinery/test.compile.pass.cpp new file mode 100644 index 00000000000..8b2e0d8218f --- /dev/null +++ b/tests/std/tests/P2278R4_ranges_const_iterator_machinery/test.compile.pass.cpp @@ -0,0 +1,166 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include + +using namespace std; + +template +concept CanIterConstRef = requires { + typename iter_const_reference_t; +}; + +template +concept CanConstIterator = requires { + typename const_iterator; +}; + +static_assert(!CanIterConstRef); +static_assert(!CanIterConstRef>); +static_assert(!CanIterConstRef>); + +namespace test_pointer { + using Ptr = int*; + static_assert(CanIterConstRef); + static_assert(CanConstIterator); + static_assert(same_as, const int&>); + static_assert(same_as, basic_const_iterator>); + static_assert(same_as>, const int&>); + static_assert(same_as, basic_const_iterator>); + static_assert(same_as>, const int&>); + + using ConstPtr = const int*; + static_assert(CanIterConstRef); + static_assert(CanConstIterator); + static_assert(same_as, const int&>); + static_assert(same_as, ConstPtr>); + static_assert(same_as>, const int&>); + static_assert(same_as, ConstPtr>); + static_assert(same_as>, const int&>); +} // namespace test_pointer + +namespace test_random_access_iter { + using Iter = deque::iterator; + static_assert(CanIterConstRef); + static_assert(CanConstIterator); + static_assert(same_as, const int&>); + static_assert(same_as, basic_const_iterator>); + static_assert(same_as>, const int&>); + static_assert(same_as, basic_const_iterator>); + static_assert(same_as>, const int&>); + + using ConstIter = deque::const_iterator; + static_assert(CanIterConstRef); + static_assert(CanConstIterator); + static_assert(same_as, const int&>); + static_assert(same_as, ConstIter>); + static_assert(same_as>, const int&>); + static_assert(same_as, ConstIter>); + static_assert(same_as>, const int&>); +} // namespace test_random_access_iter + +namespace test_bidirectional_iter { + using Iter = list::iterator; + static_assert(CanIterConstRef); + static_assert(CanConstIterator); + static_assert(same_as, const int&>); + static_assert(same_as, basic_const_iterator>); + static_assert(same_as>, const int&>); + static_assert(same_as, basic_const_iterator>); + static_assert(same_as>, const int&>); + + using ConstIter = list::const_iterator; + static_assert(CanIterConstRef); + static_assert(CanConstIterator); + static_assert(same_as, const int&>); + static_assert(same_as, ConstIter>); + static_assert(same_as>, const int&>); + static_assert(same_as, ConstIter>); + static_assert(same_as>, const int&>); +} // namespace test_bidirectional_iter + +namespace test_forward_iter { + using Iter = forward_list::iterator; + static_assert(CanIterConstRef); + static_assert(CanConstIterator); + static_assert(same_as, const int&>); + static_assert(same_as, basic_const_iterator>); + static_assert(same_as>, const int&>); + static_assert(same_as, basic_const_iterator>); + static_assert(same_as>, const int&>); + + using ConstIter = forward_list::const_iterator; + static_assert(CanIterConstRef); + static_assert(CanConstIterator); + static_assert(same_as, const int&>); + static_assert(same_as, ConstIter>); + static_assert(same_as>, const int&>); + static_assert(same_as, ConstIter>); + static_assert(same_as>, const int&>); +} // namespace test_forward_iter + +namespace test_input_iter { + using Iter = ranges::iterator_t>; + static_assert(CanIterConstRef); + static_assert(CanConstIterator); + static_assert(same_as, const int&>); + static_assert(same_as, basic_const_iterator>); + static_assert(same_as>, const int&>); + static_assert(same_as, basic_const_iterator>); + static_assert(same_as>, const int&>); +} // namespace test_input_iter + +namespace test_prvalue_iter { + using TransformView = ranges::transform_view>, int (*)(float)>; + + using Iter = ranges::iterator_t; + static_assert(CanIterConstRef); + static_assert(CanConstIterator); + static_assert(same_as, int>); + static_assert(same_as, Iter>); + static_assert(same_as>, int>); + static_assert(same_as, Iter>); + static_assert(same_as>, int>); + + using ConstIter = ranges::iterator_t; + static_assert(CanIterConstRef); + static_assert(CanConstIterator); + static_assert(same_as, int>); + static_assert(same_as, ConstIter>); + static_assert(same_as>, int>); + static_assert(same_as, ConstIter>); + static_assert(same_as>, int>); +} // namespace test_prvalue_iter + +namespace test_vector_bool_iter { + using Iter = vector::iterator; + static_assert(CanIterConstRef); + static_assert(CanConstIterator); + static_assert(same_as, bool>); + static_assert(same_as, basic_const_iterator>); + static_assert(same_as>, bool>); + static_assert(same_as, basic_const_iterator>); + static_assert(same_as>, bool>); + + using ConstIter = vector::const_iterator; + static_assert(CanIterConstRef); + static_assert(CanConstIterator); + static_assert(same_as, bool>); + static_assert(same_as, ConstIter>); + static_assert(same_as>, bool>); + static_assert(same_as, ConstIter>); + static_assert(same_as>, bool>); +} // namespace test_vector_bool_iter + +// Test standard sentinels +static_assert(!CanIterConstRef); +static_assert(!CanConstIterator); +static_assert(same_as, default_sentinel_t>); +static_assert(!CanIterConstRef); +static_assert(!CanConstIterator); +static_assert(same_as, unreachable_sentinel_t>); From ea5f25cf9470451df709739fc705b043e7fc42cc Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Fri, 19 Aug 2022 17:21:09 +0200 Subject: [PATCH 02/25] Add P2278R4_ranges_const_iterator_machinery to `test.lst` --- tests/std/test.lst | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/std/test.lst b/tests/std/test.lst index 55b77388041..39dfa9ec4ed 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -540,6 +540,7 @@ tests\P2136R3_invoke_r tests\P2162R2_std_visit_for_derived_classes_from_variant tests\P2231R1_complete_constexpr_optional_variant tests\P2273R3_constexpr_unique_ptr +tests\P2278R4_ranges_const_iterator_machinery tests\P2302R4_ranges_alg_contains tests\P2302R4_ranges_alg_contains_subrange tests\P2321R2_proxy_reference From 377c85114c7fb602cf0e5dfc7bf159ebc98e44c1 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Tue, 23 Aug 2022 03:20:33 +0200 Subject: [PATCH 03/25] CR suggestion + replace `_Implicitly_convert_to` with `_Fake_copy_init` --- stl/inc/xutility | 24 +++++++++---------- .../test.compile.pass.cpp | 2 ++ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index 5d6d4a977af..bc02ed3287e 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -1859,28 +1859,28 @@ public: _NODISCARD_FRIEND constexpr bool operator<(const basic_const_iterator& _Left, const basic_const_iterator& _Right) noexcept( - noexcept(_Implicitly_convert_to(_Left._Current < _Right._Current))) // strengthened + noexcept(_Fake_copy_init(_Left._Current < _Right._Current))) // strengthened requires random_access_iterator<_Iter> { return _Left._Current < _Right._Current; } _NODISCARD_FRIEND constexpr bool operator>(const basic_const_iterator& _Left, const basic_const_iterator& _Right) noexcept( - noexcept(_Implicitly_convert_to(_Left._Current > _Right._Current))) // strengthened + noexcept(_Fake_copy_init(_Left._Current > _Right._Current))) // strengthened requires random_access_iterator<_Iter> { return _Left._Current > _Right._Current; } _NODISCARD_FRIEND constexpr bool operator<=(const basic_const_iterator& _Left, const basic_const_iterator& _Right) noexcept( - noexcept(_Implicitly_convert_to(_Left._Current <= _Right._Current))) // strengthened + noexcept(_Fake_copy_init(_Left._Current <= _Right._Current))) // strengthened requires random_access_iterator<_Iter> { return _Left._Current <= _Right._Current; } _NODISCARD_FRIEND constexpr bool operator>=(const basic_const_iterator& _Left, const basic_const_iterator& _Right) noexcept( - noexcept(_Implicitly_convert_to(_Left._Current >= _Right._Current))) // strengthened + noexcept(_Fake_copy_init(_Left._Current >= _Right._Current))) // strengthened requires random_access_iterator<_Iter> { return _Left._Current >= _Right._Current; } @@ -1894,28 +1894,28 @@ public: template <_Not_same_as _Other> _NODISCARD_FRIEND constexpr bool operator<(const basic_const_iterator& _Left, const _Other& _Right) noexcept( - noexcept(_Implicitly_convert_to(_Left._Current < _Right))) // strengthened + noexcept(_Fake_copy_init(_Left._Current < _Right))) // strengthened requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { return _Left._Current < _Right; } template <_Not_same_as _Other> _NODISCARD_FRIEND constexpr bool operator>(const basic_const_iterator& _Left, const _Other& _Right) noexcept( - noexcept(_Implicitly_convert_to(_Left._Current > _Right))) // strengthened + noexcept(_Fake_copy_init(_Left._Current > _Right))) // strengthened requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { return _Left._Current > _Right; } template <_Not_same_as _Other> _NODISCARD_FRIEND constexpr bool operator<=(const basic_const_iterator& _Left, const _Other& _Right) noexcept( - noexcept(_Implicitly_convert_to(_Left._Current <= _Right))) // strengthened + noexcept(_Fake_copy_init(_Left._Current <= _Right))) // strengthened requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { return _Left._Current <= _Right; } template <_Not_same_as _Other> _NODISCARD_FRIEND constexpr bool operator>=(const basic_const_iterator& _Left, const _Other& _Right) noexcept( - noexcept(_Implicitly_convert_to(_Left._Current >= _Right))) // strengthened + noexcept(_Fake_copy_init(_Left._Current >= _Right))) // strengthened requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { return _Left._Current >= _Right; } @@ -1930,28 +1930,28 @@ public: template <_Not_a_const_iterator _Other> _NODISCARD_FRIEND constexpr bool operator<(const _Other& _Left, const basic_const_iterator& _Right) noexcept( - noexcept(_Implicitly_convert_to(_Left < _Right._Current))) // strengthened + noexcept(_Fake_copy_init(_Left < _Right._Current))) // strengthened requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { return _Left < _Right._Current; } template <_Not_a_const_iterator _Other> _NODISCARD_FRIEND constexpr bool operator>(const _Other& _Left, const basic_const_iterator& _Right) noexcept( - noexcept(_Implicitly_convert_to(_Left > _Right._Current))) // strengthened + noexcept(_Fake_copy_init(_Left > _Right._Current))) // strengthened requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { return _Left > _Right._Current; } template <_Not_a_const_iterator _Other> _NODISCARD_FRIEND constexpr bool operator<=(const _Other& _Left, const basic_const_iterator& _Right) noexcept( - noexcept(_Implicitly_convert_to(_Left <= _Right._Current))) // strengthened + noexcept(_Fake_copy_init(_Left <= _Right._Current))) // strengthened requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { return _Left <= _Right._Current; } template <_Not_a_const_iterator _Other> _NODISCARD_FRIEND constexpr bool operator>=(const _Other& _Left, const basic_const_iterator& _Right) noexcept( - noexcept(_Implicitly_convert_to(_Left >= _Right._Current))) // strengthened + noexcept(_Fake_copy_init(_Left >= _Right._Current))) // strengthened requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { return _Left >= _Right._Current; } diff --git a/tests/std/tests/P2278R4_ranges_const_iterator_machinery/test.compile.pass.cpp b/tests/std/tests/P2278R4_ranges_const_iterator_machinery/test.compile.pass.cpp index 8b2e0d8218f..0b532f84ea0 100644 --- a/tests/std/tests/P2278R4_ranges_const_iterator_machinery/test.compile.pass.cpp +++ b/tests/std/tests/P2278R4_ranges_const_iterator_machinery/test.compile.pass.cpp @@ -164,3 +164,5 @@ static_assert(same_as, default_sentinel_t>); static_assert(!CanIterConstRef); static_assert(!CanConstIterator); static_assert(same_as, unreachable_sentinel_t>); + +int main() {} // COMPILE-ONLY From 59db1c91d76e60b55243d95cdd148862096ef08f Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Thu, 1 Sep 2022 03:20:10 +0200 Subject: [PATCH 04/25] Add visualizer --- stl/debugger/STL.natvis | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/stl/debugger/STL.natvis b/stl/debugger/STL.natvis index b9f0787bff6..eee583f922f 100644 --- a/stl/debugger/STL.natvis +++ b/stl/debugger/STL.natvis @@ -1404,6 +1404,14 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + + basic_const_iterator {_Current} + + _Current + + + + -i*{-_Val[1]} {_Val[0]}-i*{-_Val[1]} From fdc2b293d71d50fa44c533e1eb704cf10496b1aa Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Fri, 9 Sep 2022 00:31:57 +0200 Subject: [PATCH 05/25] Implement test coverage --- tests/std/test.lst | 1 + .../P2278R4_basic_const_iterator/env.lst | 4 + .../P2278R4_basic_const_iterator/test.cpp | 295 ++++++++++++++++++ 3 files changed, 300 insertions(+) create mode 100644 tests/std/tests/P2278R4_basic_const_iterator/env.lst create mode 100644 tests/std/tests/P2278R4_basic_const_iterator/test.cpp diff --git a/tests/std/test.lst b/tests/std/test.lst index 04bcf944c95..69de5b38c1f 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -543,6 +543,7 @@ tests\P2136R3_invoke_r tests\P2162R2_std_visit_for_derived_classes_from_variant tests\P2231R1_complete_constexpr_optional_variant tests\P2273R3_constexpr_unique_ptr +tests\P2278R4_basic_const_iterator tests\P2278R4_ranges_const_iterator_machinery tests\P2302R4_ranges_alg_contains tests\P2302R4_ranges_alg_contains_subrange diff --git a/tests/std/tests/P2278R4_basic_const_iterator/env.lst b/tests/std/tests/P2278R4_basic_const_iterator/env.lst new file mode 100644 index 00000000000..18e2d7c71ec --- /dev/null +++ b/tests/std/tests/P2278R4_basic_const_iterator/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_latest_matrix.lst diff --git a/tests/std/tests/P2278R4_basic_const_iterator/test.cpp b/tests/std/tests/P2278R4_basic_const_iterator/test.cpp new file mode 100644 index 00000000000..2c0f98ca6fa --- /dev/null +++ b/tests/std/tests/P2278R4_basic_const_iterator/test.cpp @@ -0,0 +1,295 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include + +#include "range_algorithm_support.hpp" + +using namespace std; + +template +concept HasPeek = requires(It iter) { + { iter.peek() } -> same_as>>; +}; + +template +constexpr void test_one(It iter) { + using ConstIt = basic_const_iterator; + using Se = test::sentinel>; + + // Validate iterator concepts + static_assert(input_iterator); + static_assert(forward_iterator == forward_iterator); + static_assert(bidirectional_iterator == bidirectional_iterator); + static_assert(random_access_iterator == random_access_iterator); + static_assert(contiguous_iterator == contiguous_iterator); + + // Validate nested types + static_assert(same_as>); + static_assert(same_as>); + if constexpr (forward_iterator) { + using Cat = typename iterator_traits::iterator_category; + static_assert(same_as); + } + + // Validate default-initializability + static_assert(default_initializable == default_initializable); + + same_as auto citer = basic_const_iterator{iter}; + if (!forward_iterator) { // intentionally not if constexpr + return; + } + + // Validate basic_const_iterator::operator*() + { + same_as> decltype(auto) val = *citer; + assert(val == *iter); + static_assert(noexcept(*citer) == noexcept(static_cast>(*iter))); // strengthened + } + + // Validate basic_const_iterator::operator->() + if constexpr (is_lvalue_reference_v> && // + same_as>, iter_value_t>) { + const same_as*> auto ptr = citer.operator->(); + assert(ptr == std::addressof(*citer)); + + if constexpr (contiguous_iterator) { + static_assert(noexcept(citer.operator->()) == noexcept(std::to_address(iter))); // strengthened + } else { + static_assert(noexcept(citer.operator->()) == noexcept(std::addressof(*iter))); // strengthened + } + } + + // Validate basic_const_iterator::operator++() + { + same_as decltype(auto) citer2 = ++citer; + ++iter; + assert(citer2 == iter); + assert(*citer2 == *iter); + static_assert(noexcept(++citer) == noexcept(++iter)); // strengthened + } + + // Validate basic_const_iterator::operator++(int) + { + if constexpr (forward_iterator) { + same_as decltype(auto) citer2 = citer++; + assert(citer2 == iter); + assert(*citer2 == *iter); + static_assert( + noexcept(citer++) == (noexcept(iter++) && is_nothrow_copy_constructible_v) ); // strengthened + } else { + static_assert(is_void_v); + static_assert(noexcept(citer++) == noexcept(iter++)); // strengthened + } + + iter++; + assert(citer == iter); + assert(*citer == *iter); + } + + if constexpr (bidirectional_iterator) { + // Validate basic_const_iterator::operator--() + { + same_as decltype(auto) citer2 = --citer; + --iter; + assert(citer2 == iter); + assert(*citer2 == *iter); + static_assert(noexcept(--citer) == noexcept(--iter)); // strengthened + } + + // Validate basic_const_iterator::operator--(int) + { + same_as decltype(auto) citer2 = citer--; + assert(citer2 == iter); + assert(*citer2 == *iter); + static_assert( + noexcept(citer--) == (noexcept(iter--) && is_nothrow_copy_constructible_v) ); // strengthened + + iter--; + assert(citer == iter); + assert(*citer == *iter); + } + } + + if constexpr (random_access_iterator) { + // Validate basic_const_iterator::operator+=() + { + same_as decltype(auto) citer2 = (citer += 2); + iter += 2; + assert(citer2 == iter); + assert(*citer2 == *iter); + static_assert(noexcept(citer += 2) == noexcept(iter += 2)); // strengthened + } + + // Validate basic_const_iterator::operator-=() + { + same_as decltype(auto) citer2 = (citer -= 2); + iter -= 2; + assert(citer2 == iter); + assert(*citer2 == *iter); + static_assert(noexcept(citer -= 2) == noexcept(iter -= 2)); // strengthened + } + + // Validate basic_const_iterator::operator[] + { + same_as> decltype(auto) val = citer[0]; + assert(val == iter[0]); + static_assert( + noexcept(citer[0]) == noexcept(static_cast>(iter[0]))); // strengthened + } + + // Validate operator{<, >, <=, >=, <=>}(const basic_const_iterator&, const basic_const_iterator&) + { + const same_as auto citer2 = (citer + 1); + assert(citer < citer2); + assert(citer2 > citer); + assert(citer <= citer2); + assert(citer2 >= citer); + assert(citer2 <=> citer == partial_ordering::greater); + + static_assert(noexcept(citer < citer2) == noexcept(citer.base() < citer2.base())); // strengthened + static_assert(noexcept(citer2 > citer) == noexcept(citer2.base() > citer.base())); // strengthened + static_assert(noexcept(citer <= citer2) == noexcept(citer.base() <= citer2.base())); // strengthened + static_assert(noexcept(citer2 >= citer) == noexcept(citer2.base() >= citer.base())); // strengthened + static_assert(noexcept(citer2 <=> citer) == noexcept(citer2.base() <=> citer.base())); // strengthened + } + + iter += 2; // advance iter temporarily + + // Validate operator{<, >, <=, >=, <=>}(const basic_const_iterator&, const "not same as basic_const_iterator"&) + { + assert(citer < iter); + assert(!(citer > iter)); + assert(citer <= iter); + assert(!(citer >= iter)); + assert(citer <=> iter == partial_ordering::less); + + static_assert(noexcept(citer < iter) == noexcept(citer.base() < iter)); // strengthened + static_assert(noexcept(!(citer > iter)) == noexcept(!(citer.base() > iter))); // strengthened + static_assert(noexcept(citer <= iter) == noexcept(citer.base() <= iter)); // strengthened + static_assert(noexcept(!(citer >= iter)) == noexcept(!(citer.base() >= iter))); // strengthened + static_assert(noexcept(citer <=> iter) == noexcept(citer.base() <=> iter)); // strengthened + } + + // Validate operator{<, >, <=, >=}(const "not a const iterator"&, const basic_const_iterator&) + { + assert(!(iter < citer)); + assert(iter > citer); + assert(!(iter <= citer)); + assert(iter >= citer); + + static_assert(noexcept(!(iter < citer)) == noexcept(!(iter < citer.base()))); // strengthened + static_assert(noexcept(iter > citer) == noexcept(iter > citer.base())); // strengthened + static_assert(noexcept(!(iter <= citer)) == noexcept(!(iter <= citer.base()))); // strengthened + static_assert(noexcept(iter >= citer) == noexcept(iter >= citer.base())); // strengthened + } + + // Validate operator+(const basic_const_iterator&, difference_type) + { + const same_as auto citer2 = (citer + 2); + const same_as auto citer3 = (2 + citer); + assert(citer2 == citer3); + assert(*citer2 == *iter); + + static_assert(noexcept(citer + 2) == noexcept(iter + 2)); // strengthened + static_assert(noexcept(2 + citer) == noexcept(2 + iter)); // strengthened + } + + // Validate operator-(const basic_const_iterator&, difference_type) + { + citer += 4; + const same_as auto citer2 = (citer - 2); + assert(*citer2 == *iter); + citer -= 4; + + static_assert(noexcept(citer - 2) == noexcept(iter - 2)); // strengthened + } + + // Validate operator-(const basic_const_iterator&, sized_sentinel) + { + assert(citer - citer == 0); + assert(citer - iter == -2); + assert(iter - citer == 2); + static_assert(noexcept(citer - citer) == noexcept(iter - iter)); // strengthened + static_assert(noexcept(citer - iter) == noexcept(iter - iter)); // strengthened + static_assert(noexcept(iter - citer) == noexcept(iter - iter)); // strengthened + + if constexpr (HasPeek && sized_sentinel_for) { + Se sent{iter.peek()}; + assert(citer - sent == -2); + assert(sent - citer == 2); + static_assert(noexcept(citer - sent) == noexcept(iter - sent)); // strengthened + static_assert(noexcept(sent - citer) == noexcept(sent - iter)); // strengthened + } + } + + iter -= 2; + } + + // Validate to_address + if constexpr (contiguous_iterator && HasPeek) { + assert(std::to_address(citer) == iter.peek()); + } + + // Validate basic_const_iterator::operator==() + assert(citer == iter); + assert(iter == citer); + static_assert(noexcept(citer == iter) == noexcept(iter == iter)); + if constexpr (HasPeek && sentinel_for) { + Se sent{iter.peek()}; + assert(citer == sent); + static_assert(noexcept(citer == sent) == noexcept(iter == sent)); // strengthened + } + + // Validate basic_const_iterator::base() const& + { + [[maybe_unused]] same_as decltype(auto) base = citer.base(); + static_assert(noexcept(citer.base())); + } + + // Validate basic_const_iterator::base() && + { + [[maybe_unused]] same_as decltype(auto) base = std::move(citer).base(); + static_assert(noexcept(std::move(citer).base()) == is_nothrow_move_constructible_v); // strengthened + } +} + +static constexpr int some_ints[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + +struct instantiator { + template + static constexpr void call() { + It iter{some_ints}; + test_one(iter); + } +}; + +template +using test_iterator = test::iterator}>; + +constexpr void instantiation_test() { + using test::CanDifference; + + // The iterator is sensitive to category and differencing + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); +} + +int main() { + static_assert((instantiation_test(), true)); + instantiation_test(); +} From 57c17d765733ae1da85ec1372af9876a600a2da7 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Fri, 9 Sep 2022 00:35:04 +0200 Subject: [PATCH 06/25] NON CONFORMING: remove most of comparison functions and *dissolve* some friends --- stl/inc/xutility | 197 +++++++++++++++++++++++------------------------ 1 file changed, 97 insertions(+), 100 deletions(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index 28acb101347..330883bff60 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -1853,109 +1853,106 @@ public: } template _Sent> - _NODISCARD_FRIEND constexpr bool operator==(const basic_const_iterator& _It, const _Sent& _Se) noexcept( - noexcept(_It._Current == _Se)) /* strengthened */ { - return _It._Current == _Se; + _NODISCARD constexpr bool operator==(const _Sent& _Se) const // FIXME: non conforming + noexcept(noexcept(_Current == _Se)) /* strengthened */ { + return _Current == _Se; } - _NODISCARD_FRIEND constexpr bool - operator<(const basic_const_iterator& _Left, const basic_const_iterator& _Right) noexcept( - noexcept(_Fake_copy_init(_Left._Current < _Right._Current))) // strengthened + _NODISCARD constexpr bool operator<(const basic_const_iterator& _Right) const noexcept( // FIXME: non conforming + noexcept(_Fake_copy_init(_Current < _Right._Current))) // strengthened requires random_access_iterator<_Iter> { - return _Left._Current < _Right._Current; + return _Current < _Right._Current; } - _NODISCARD_FRIEND constexpr bool - operator>(const basic_const_iterator& _Left, const basic_const_iterator& _Right) noexcept( - noexcept(_Fake_copy_init(_Left._Current > _Right._Current))) // strengthened + _NODISCARD constexpr bool operator>(const basic_const_iterator& _Right) const noexcept( // FIXME: non conforming + noexcept(_Fake_copy_init(_Current > _Right._Current))) // strengthened requires random_access_iterator<_Iter> { - return _Left._Current > _Right._Current; + return _Current > _Right._Current; } - _NODISCARD_FRIEND constexpr bool - operator<=(const basic_const_iterator& _Left, const basic_const_iterator& _Right) noexcept( - noexcept(_Fake_copy_init(_Left._Current <= _Right._Current))) // strengthened + _NODISCARD constexpr bool operator<=(const basic_const_iterator& _Right) const noexcept( // FIXME: non conforming + noexcept(_Fake_copy_init(_Current <= _Right._Current))) // strengthened requires random_access_iterator<_Iter> { - return _Left._Current <= _Right._Current; + return _Current <= _Right._Current; } - _NODISCARD_FRIEND constexpr bool - operator>=(const basic_const_iterator& _Left, const basic_const_iterator& _Right) noexcept( - noexcept(_Fake_copy_init(_Left._Current >= _Right._Current))) // strengthened + _NODISCARD constexpr bool operator>=(const basic_const_iterator& _Right) const noexcept( // FIXME: non conforming + noexcept(_Fake_copy_init(_Current >= _Right._Current))) // strengthened requires random_access_iterator<_Iter> { - return _Left._Current >= _Right._Current; + return _Current >= _Right._Current; } - _NODISCARD_FRIEND constexpr auto // - operator<=>(const basic_const_iterator& _Left, const basic_const_iterator& _Right) noexcept( - noexcept(_Left._Current <=> _Right._Current)) // strengthened + _NODISCARD constexpr auto // + operator<=>(const basic_const_iterator& _Right) const noexcept( // FIXME: non conforming + noexcept(_Current <=> _Right._Current)) // strengthened requires random_access_iterator<_Iter> && three_way_comparable<_Iter> { - return _Left._Current <=> _Right._Current; - } - - template <_Not_same_as _Other> - _NODISCARD_FRIEND constexpr bool operator<(const basic_const_iterator& _Left, const _Other& _Right) noexcept( - noexcept(_Fake_copy_init(_Left._Current < _Right))) // strengthened - requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { - return _Left._Current < _Right; - } - - template <_Not_same_as _Other> - _NODISCARD_FRIEND constexpr bool operator>(const basic_const_iterator& _Left, const _Other& _Right) noexcept( - noexcept(_Fake_copy_init(_Left._Current > _Right))) // strengthened - requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { - return _Left._Current > _Right; - } - - template <_Not_same_as _Other> - _NODISCARD_FRIEND constexpr bool operator<=(const basic_const_iterator& _Left, const _Other& _Right) noexcept( - noexcept(_Fake_copy_init(_Left._Current <= _Right))) // strengthened - requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { - return _Left._Current <= _Right; - } - - template <_Not_same_as _Other> - _NODISCARD_FRIEND constexpr bool operator>=(const basic_const_iterator& _Left, const _Other& _Right) noexcept( - noexcept(_Fake_copy_init(_Left._Current >= _Right))) // strengthened - requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { - return _Left._Current >= _Right; - } - - template <_Not_same_as _Other> - _NODISCARD_FRIEND constexpr auto operator<=>(const basic_const_iterator& _Left, const _Other& _Right) noexcept( - noexcept(_Left._Current <=> _Right)) // strengthened - requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> && // - three_way_comparable_with<_Iter, _Other> { - return _Left._Current <=> _Right; - } - - template <_Not_a_const_iterator _Other> - _NODISCARD_FRIEND constexpr bool operator<(const _Other& _Left, const basic_const_iterator& _Right) noexcept( - noexcept(_Fake_copy_init(_Left < _Right._Current))) // strengthened - requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { - return _Left < _Right._Current; - } - - template <_Not_a_const_iterator _Other> - _NODISCARD_FRIEND constexpr bool operator>(const _Other& _Left, const basic_const_iterator& _Right) noexcept( - noexcept(_Fake_copy_init(_Left > _Right._Current))) // strengthened - requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { - return _Left > _Right._Current; - } - - template <_Not_a_const_iterator _Other> - _NODISCARD_FRIEND constexpr bool operator<=(const _Other& _Left, const basic_const_iterator& _Right) noexcept( - noexcept(_Fake_copy_init(_Left <= _Right._Current))) // strengthened - requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { - return _Left <= _Right._Current; - } - - template <_Not_a_const_iterator _Other> - _NODISCARD_FRIEND constexpr bool operator>=(const _Other& _Left, const basic_const_iterator& _Right) noexcept( - noexcept(_Fake_copy_init(_Left >= _Right._Current))) // strengthened - requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { - return _Left >= _Right._Current; - } + return _Current <=> _Right._Current; + } + + // FIXME: removing those functions in non conforming + // template <_Not_same_as _Other> + // _NODISCARD_FRIEND constexpr bool operator<(const basic_const_iterator& _Left, const _Other& _Right) noexcept( + // noexcept(_Fake_copy_init(_Left._Current < _Right))) // strengthened + // requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { + // return _Left._Current < _Right; + // } + + // template <_Not_same_as _Other> + // _NODISCARD_FRIEND constexpr bool operator>(const basic_const_iterator& _Left, const _Other& _Right) noexcept( + // noexcept(_Fake_copy_init(_Left._Current > _Right))) // strengthened + // requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { + // return _Left._Current > _Right; + // } + + // template <_Not_same_as _Other> + // _NODISCARD_FRIEND constexpr bool operator<=(const basic_const_iterator& _Left, const _Other& _Right) noexcept( + // noexcept(_Fake_copy_init(_Left._Current <= _Right))) // strengthened + // requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { + // return _Left._Current <= _Right; + // } + + // template <_Not_same_as _Other> + // _NODISCARD_FRIEND constexpr bool operator>=(const basic_const_iterator& _Left, const _Other& _Right) noexcept( + // noexcept(_Fake_copy_init(_Left._Current >= _Right))) // strengthened + // requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { + // return _Left._Current >= _Right; + // } + + // template <_Not_same_as _Other> + // _NODISCARD_FRIEND constexpr auto operator<=>(const basic_const_iterator& _Left, const _Other& _Right) noexcept( + // noexcept(_Left._Current <=> _Right)) // strengthened + // requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> && // + // three_way_comparable_with<_Iter, _Other> { + // return _Left._Current <=> _Right; + // } + + // template <_Not_a_const_iterator _Other> + // _NODISCARD_FRIEND constexpr bool operator<(const _Other& _Left, const basic_const_iterator& _Right) noexcept( + // noexcept(_Fake_copy_init(_Left < _Right._Current))) // strengthened + // requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { + // return _Left < _Right._Current; + // } + + // template <_Not_a_const_iterator _Other> + // _NODISCARD_FRIEND constexpr bool operator>(const _Other& _Left, const basic_const_iterator& _Right) noexcept( + // noexcept(_Fake_copy_init(_Left > _Right._Current))) // strengthened + // requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { + // return _Left > _Right._Current; + // } + + // template <_Not_a_const_iterator _Other> + // _NODISCARD_FRIEND constexpr bool operator<=(const _Other& _Left, const basic_const_iterator& _Right) noexcept( + // noexcept(_Fake_copy_init(_Left <= _Right._Current))) // strengthened + // requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { + // return _Left <= _Right._Current; + // } + + // template <_Not_a_const_iterator _Other> + // _NODISCARD_FRIEND constexpr bool operator>=(const _Other& _Left, const basic_const_iterator& _Right) noexcept( + // noexcept(_Fake_copy_init(_Left >= _Right._Current))) // strengthened + // requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { + // return _Left >= _Right._Current; + //} _NODISCARD_FRIEND constexpr basic_const_iterator operator+( const basic_const_iterator& _It, const difference_type _Off) // @@ -1979,21 +1976,21 @@ public: } template _Sent> - _NODISCARD_FRIEND constexpr difference_type operator-(const basic_const_iterator& _It, const _Sent& _Se) noexcept( - noexcept(_It._Current - _Se)) /* strengthened */ { - return _It._Current - _Se; - } - - // clang-format off - template _Sent> - requires _Not_same_as<_Sent, basic_const_iterator> - _NODISCARD_FRIEND constexpr difference_type operator-(const _Sent& _Se, const basic_const_iterator& _It) - noexcept(noexcept(_Se - _It._Current)) /* strengthened */ { - // clang-format on - return _Se - _It._Current; + _NODISCARD constexpr difference_type operator-(const _Sent& _Se) const // FIXME: non conforming + noexcept(noexcept(_Current - _Se)) /* strengthened */ { + return _Current - _Se; } }; +// clang-format off +template _Sent> // FIXME: non conforming + requires _Not_same_as<_Sent, basic_const_iterator<_Iter>> +_NODISCARD constexpr iter_difference_t<_Iter> operator-(const _Sent& _Se, const basic_const_iterator<_Iter>& _It) + noexcept(noexcept(_Se - _It.base())) /* strengthened */ { + // clang-format on + return _Se - _It.base(); +} + template _Ty2> struct common_type, _Ty2> { using type = basic_const_iterator>; From 2f9fe812b58834c7504ef1d39d96f5963794dea0 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Thu, 15 Sep 2022 10:16:09 -0700 Subject: [PATCH 07/25] Replace bespoke _Is_basic_const_iterator_v with _Is_specialization_v --- stl/inc/xutility | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index 330883bff60..90c016b1324 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -1717,14 +1717,8 @@ struct _Const_sentinel<_Sent> { template using const_sentinel = typename _Const_sentinel<_Sent>::type; -template -inline constexpr bool _Is_basic_const_iterator_v = false; - -template -inline constexpr bool _Is_basic_const_iterator_v> = true; - template -concept _Not_a_const_iterator = !_Is_basic_const_iterator_v<_Ty>; +concept _Not_a_const_iterator = !_Is_specialization_v<_Ty, basic_const_iterator>; template struct _Basic_const_iterator_category {}; From 1a6f08e4345d5dd1ba0c045836e4af812cbc7a55 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Thu, 15 Sep 2022 10:16:23 -0700 Subject: [PATCH 08/25] Fix comment typo --- tests/std/include/range_algorithm_support.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/include/range_algorithm_support.hpp b/tests/std/include/range_algorithm_support.hpp index 3301b6371b5..a65e0ca6d42 100644 --- a/tests/std/include/range_algorithm_support.hpp +++ b/tests/std/include/range_algorithm_support.hpp @@ -353,7 +353,7 @@ namespace test { template }, - // Model sentinel_for with self (and sized_sentinel_for if Diff; implies copyable)? + // Model sentinel_for with self (and sized_sentinel_for if Diff implies copyable)? CanCompare Eq = CanCompare{derived_from}, // Use a ProxyRef reference type (instead of Element&)? ProxyRef Proxy = ProxyRef{!derived_from}, From 9771706f1e0f8a1f231e5252a6ce1ba2ef3461c2 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Thu, 15 Sep 2022 10:18:08 -0700 Subject: [PATCH 09/25] Add missing `_Fake_copy_init` to account for conversion to bool in `noexcept` --- stl/inc/xutility | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index 90c016b1324..b996228f7d7 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -1848,7 +1848,7 @@ public: template _Sent> _NODISCARD constexpr bool operator==(const _Sent& _Se) const // FIXME: non conforming - noexcept(noexcept(_Current == _Se)) /* strengthened */ { + noexcept(noexcept(_Fake_copy_init(_Current == _Se))) /* strengthened */ { return _Current == _Se; } From b87697b038ed274d6c3b3df366ae84711bde650e Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Thu, 15 Sep 2022 10:19:42 -0700 Subject: [PATCH 10/25] Implement comparisons per the proposed resolution of LWG-3769 --- stl/inc/xutility | 205 +++++++++++++++++++++++------------------------ 1 file changed, 102 insertions(+), 103 deletions(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index b996228f7d7..3fdecca5963 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -1847,143 +1847,142 @@ public: } template _Sent> - _NODISCARD constexpr bool operator==(const _Sent& _Se) const // FIXME: non conforming + _NODISCARD constexpr bool operator==(const _Sent& _Se) const // per LWG-3769 noexcept(noexcept(_Fake_copy_init(_Current == _Se))) /* strengthened */ { return _Current == _Se; } - _NODISCARD constexpr bool operator<(const basic_const_iterator& _Right) const noexcept( // FIXME: non conforming - noexcept(_Fake_copy_init(_Current < _Right._Current))) // strengthened + _NODISCARD_FRIEND constexpr bool + operator<(const basic_const_iterator& _Left, const basic_const_iterator& _Right) noexcept( + noexcept(_Fake_copy_init(_Left._Current < _Right._Current))) // strengthened requires random_access_iterator<_Iter> { - return _Current < _Right._Current; + return _Left._Current < _Right._Current; } - _NODISCARD constexpr bool operator>(const basic_const_iterator& _Right) const noexcept( // FIXME: non conforming - noexcept(_Fake_copy_init(_Current > _Right._Current))) // strengthened + _NODISCARD_FRIEND constexpr bool + operator>(const basic_const_iterator& _Left, const basic_const_iterator& _Right) noexcept( + noexcept(_Fake_copy_init(_Left._Current > _Right._Current))) // strengthened requires random_access_iterator<_Iter> { - return _Current > _Right._Current; + return _Left._Current > _Right._Current; } - _NODISCARD constexpr bool operator<=(const basic_const_iterator& _Right) const noexcept( // FIXME: non conforming - noexcept(_Fake_copy_init(_Current <= _Right._Current))) // strengthened + _NODISCARD_FRIEND constexpr bool + operator<=(const basic_const_iterator& _Left, const basic_const_iterator& _Right) noexcept( + noexcept(_Fake_copy_init(_Left._Current <= _Right._Current))) // strengthened requires random_access_iterator<_Iter> { - return _Current <= _Right._Current; + return _Left._Current <= _Right._Current; } - _NODISCARD constexpr bool operator>=(const basic_const_iterator& _Right) const noexcept( // FIXME: non conforming - noexcept(_Fake_copy_init(_Current >= _Right._Current))) // strengthened + _NODISCARD_FRIEND constexpr bool + operator>=(const basic_const_iterator& _Left, const basic_const_iterator& _Right) noexcept( + noexcept(_Fake_copy_init(_Left._Current >= _Right._Current))) // strengthened requires random_access_iterator<_Iter> { - return _Current >= _Right._Current; + return _Left._Current >= _Right._Current; } - _NODISCARD constexpr auto // - operator<=>(const basic_const_iterator& _Right) const noexcept( // FIXME: non conforming - noexcept(_Current <=> _Right._Current)) // strengthened + _NODISCARD_FRIEND constexpr auto operator<=>(const basic_const_iterator& _Left, + const basic_const_iterator& _Right) noexcept(noexcept(_Left._Current <=> _Right._Current)) // strengthened requires random_access_iterator<_Iter> && three_way_comparable<_Iter> { - return _Current <=> _Right._Current; - } - - // FIXME: removing those functions in non conforming - // template <_Not_same_as _Other> - // _NODISCARD_FRIEND constexpr bool operator<(const basic_const_iterator& _Left, const _Other& _Right) noexcept( - // noexcept(_Fake_copy_init(_Left._Current < _Right))) // strengthened - // requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { - // return _Left._Current < _Right; - // } - - // template <_Not_same_as _Other> - // _NODISCARD_FRIEND constexpr bool operator>(const basic_const_iterator& _Left, const _Other& _Right) noexcept( - // noexcept(_Fake_copy_init(_Left._Current > _Right))) // strengthened - // requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { - // return _Left._Current > _Right; - // } - - // template <_Not_same_as _Other> - // _NODISCARD_FRIEND constexpr bool operator<=(const basic_const_iterator& _Left, const _Other& _Right) noexcept( - // noexcept(_Fake_copy_init(_Left._Current <= _Right))) // strengthened - // requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { - // return _Left._Current <= _Right; - // } - - // template <_Not_same_as _Other> - // _NODISCARD_FRIEND constexpr bool operator>=(const basic_const_iterator& _Left, const _Other& _Right) noexcept( - // noexcept(_Fake_copy_init(_Left._Current >= _Right))) // strengthened - // requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { - // return _Left._Current >= _Right; - // } - - // template <_Not_same_as _Other> - // _NODISCARD_FRIEND constexpr auto operator<=>(const basic_const_iterator& _Left, const _Other& _Right) noexcept( - // noexcept(_Left._Current <=> _Right)) // strengthened - // requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> && // - // three_way_comparable_with<_Iter, _Other> { - // return _Left._Current <=> _Right; - // } - - // template <_Not_a_const_iterator _Other> - // _NODISCARD_FRIEND constexpr bool operator<(const _Other& _Left, const basic_const_iterator& _Right) noexcept( - // noexcept(_Fake_copy_init(_Left < _Right._Current))) // strengthened - // requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { - // return _Left < _Right._Current; - // } - - // template <_Not_a_const_iterator _Other> - // _NODISCARD_FRIEND constexpr bool operator>(const _Other& _Left, const basic_const_iterator& _Right) noexcept( - // noexcept(_Fake_copy_init(_Left > _Right._Current))) // strengthened - // requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { - // return _Left > _Right._Current; - // } - - // template <_Not_a_const_iterator _Other> - // _NODISCARD_FRIEND constexpr bool operator<=(const _Other& _Left, const basic_const_iterator& _Right) noexcept( - // noexcept(_Fake_copy_init(_Left <= _Right._Current))) // strengthened - // requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { - // return _Left <= _Right._Current; - // } - - // template <_Not_a_const_iterator _Other> - // _NODISCARD_FRIEND constexpr bool operator>=(const _Other& _Left, const basic_const_iterator& _Right) noexcept( - // noexcept(_Fake_copy_init(_Left >= _Right._Current))) // strengthened - // requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { - // return _Left >= _Right._Current; - //} - - _NODISCARD_FRIEND constexpr basic_const_iterator operator+( - const basic_const_iterator& _It, const difference_type _Off) // - noexcept(noexcept(basic_const_iterator{_It._Current + _Off})) // strengthened + return _Left._Current <=> _Right._Current; + } + + template <_Not_same_as _Other> + _NODISCARD_FRIEND constexpr bool operator<(const basic_const_iterator& _Left, const _Other& _Right) noexcept( + noexcept(_Fake_copy_init(_Left._Current < _Right))) // strengthened + requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { + return _Left._Current < _Right; + } + + template <_Not_same_as _Other> + _NODISCARD_FRIEND constexpr bool operator>(const basic_const_iterator& _Left, const _Other& _Right) noexcept( + noexcept(_Fake_copy_init(_Left._Current > _Right))) // strengthened + requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { + return _Left._Current > _Right; + } + + template <_Not_same_as _Other> + _NODISCARD_FRIEND constexpr bool operator<=(const basic_const_iterator& _Left, const _Other& _Right) noexcept( + noexcept(_Fake_copy_init(_Left._Current <= _Right))) // strengthened + requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { + return _Left._Current <= _Right; + } + + template <_Not_same_as _Other> + _NODISCARD_FRIEND constexpr bool operator>=(const basic_const_iterator& _Left, const _Other& _Right) noexcept( + noexcept(_Fake_copy_init(_Left._Current >= _Right))) // strengthened + requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { + return _Left._Current >= _Right; + } + + template <_Not_a_const_iterator _Other> + _NODISCARD_FRIEND constexpr bool operator<(const _Other& _Left, const basic_const_iterator& _Right) noexcept( + noexcept(_Fake_copy_init(_Left < _Right._Current))) // strengthened + requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { + return _Left < _Right._Current; + } + + template <_Not_a_const_iterator _Other> + _NODISCARD_FRIEND constexpr bool operator>(const _Other& _Left, const basic_const_iterator& _Right) noexcept( + noexcept(_Fake_copy_init(_Left > _Right._Current))) // strengthened + requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { + return _Left > _Right._Current; + } + + template <_Not_a_const_iterator _Other> + _NODISCARD_FRIEND constexpr bool operator<=(const _Other& _Left, const basic_const_iterator& _Right) noexcept( + noexcept(_Fake_copy_init(_Left <= _Right._Current))) // strengthened + requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { + return _Left <= _Right._Current; + } + + template <_Not_a_const_iterator _Other> + _NODISCARD_FRIEND constexpr bool operator>=(const _Other& _Left, const basic_const_iterator& _Right) noexcept( + noexcept(_Fake_copy_init(_Left >= _Right._Current))) // strengthened + requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { + return _Left >= _Right._Current; + } + + template <_Not_same_as _Other> + _NODISCARD_FRIEND constexpr auto operator<=>(const basic_const_iterator& _Left, const _Other& _Right) noexcept( + noexcept(_Left._Current <=> _Right)) // strengthened + requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> && // + three_way_comparable_with<_Iter, _Other> { + return _Left._Current <=> _Right; + } + + _NODISCARD_FRIEND constexpr basic_const_iterator operator+(const basic_const_iterator& _It, + const difference_type _Off) noexcept(noexcept(basic_const_iterator{_It._Current + _Off})) // strengthened requires random_access_iterator<_Iter> { return basic_const_iterator{_It._Current + _Off}; } - _NODISCARD_FRIEND constexpr basic_const_iterator operator+( - const difference_type _Off, const basic_const_iterator& _It) // - noexcept(noexcept(basic_const_iterator{_It._Current + _Off})) // strengthened + _NODISCARD_FRIEND constexpr basic_const_iterator operator+(const difference_type _Off, + const basic_const_iterator& _It) noexcept(noexcept(basic_const_iterator{_It._Current + _Off})) // strengthened requires random_access_iterator<_Iter> { return basic_const_iterator{_It._Current + _Off}; } - _NODISCARD_FRIEND constexpr basic_const_iterator operator-( - const basic_const_iterator& _It, const difference_type _Off) // - noexcept(noexcept(basic_const_iterator{_It._Current - _Off})) // strengthened + _NODISCARD_FRIEND constexpr basic_const_iterator operator-(const basic_const_iterator& _It, + const difference_type _Off) noexcept(noexcept(basic_const_iterator{_It._Current - _Off})) // strengthened requires random_access_iterator<_Iter> { return basic_const_iterator{_It._Current - _Off}; } template _Sent> - _NODISCARD constexpr difference_type operator-(const _Sent& _Se) const // FIXME: non conforming + _NODISCARD constexpr difference_type operator-(const _Sent& _Se) const // per LWG-3769 noexcept(noexcept(_Current - _Se)) /* strengthened */ { return _Current - _Se; } -}; -// clang-format off -template _Sent> // FIXME: non conforming - requires _Not_same_as<_Sent, basic_const_iterator<_Iter>> -_NODISCARD constexpr iter_difference_t<_Iter> operator-(const _Sent& _Se, const basic_const_iterator<_Iter>& _It) - noexcept(noexcept(_Se - _It.base())) /* strengthened */ { - // clang-format on - return _Se - _It.base(); -} + // clang-format off + template <_Not_a_const_iterator _Sent> + requires sized_sentinel_for<_Sent, _Iter> + _NODISCARD_FRIEND constexpr difference_type operator-(const _Sent& _Se, const basic_const_iterator& _It) noexcept( + noexcept(_Se - _It.base())) /* strengthened */ { // per LWG-3769 + // clang-format on + return _Se - _It.base(); + } +}; template _Ty2> struct common_type, _Ty2> { From 66a41b81a2617df4bb0ebaf131013996ce1f5961 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Thu, 15 Sep 2022 12:09:08 -0700 Subject: [PATCH 11/25] Factor out some requirements into named concepts to workaround LLVM-55945 --- stl/inc/xutility | 58 +++++++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index 3fdecca5963..1baecb8d836 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -1728,6 +1728,18 @@ struct _Basic_const_iterator_category<_Iter> { using iterator_category = typename iterator_traits<_Iter>::iterator_category; }; +// These are distinct concepts to workaround LLVM-55945 +template +concept _Bci_order = + _Not_same_as<_Ty, basic_const_iterator<_Iter>> && random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Ty>; + +template +concept _Bci_order_3way = _Bci_order<_Ty, _Iter> && three_way_comparable_with<_Iter, _Ty>; + +template +concept _Not_bci_order = + _Not_a_const_iterator<_Ty> && random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Ty>; + template class basic_const_iterator : public _Basic_const_iterator_category<_Iter> { private: @@ -1886,67 +1898,57 @@ public: return _Left._Current <=> _Right._Current; } - template <_Not_same_as _Other> + template <_Bci_order<_Iter> _Other> _NODISCARD_FRIEND constexpr bool operator<(const basic_const_iterator& _Left, const _Other& _Right) noexcept( - noexcept(_Fake_copy_init(_Left._Current < _Right))) // strengthened - requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { + noexcept(_Fake_copy_init(_Left._Current < _Right))) /* strengthened */ { return _Left._Current < _Right; } - template <_Not_same_as _Other> + template <_Bci_order<_Iter> _Other> _NODISCARD_FRIEND constexpr bool operator>(const basic_const_iterator& _Left, const _Other& _Right) noexcept( - noexcept(_Fake_copy_init(_Left._Current > _Right))) // strengthened - requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { + noexcept(_Fake_copy_init(_Left._Current > _Right))) /* strengthened */ { return _Left._Current > _Right; } - template <_Not_same_as _Other> + template <_Bci_order<_Iter> _Other> _NODISCARD_FRIEND constexpr bool operator<=(const basic_const_iterator& _Left, const _Other& _Right) noexcept( - noexcept(_Fake_copy_init(_Left._Current <= _Right))) // strengthened - requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { + noexcept(_Fake_copy_init(_Left._Current <= _Right))) /* strengthened */ { return _Left._Current <= _Right; } - template <_Not_same_as _Other> + template <_Bci_order<_Iter> _Other> _NODISCARD_FRIEND constexpr bool operator>=(const basic_const_iterator& _Left, const _Other& _Right) noexcept( - noexcept(_Fake_copy_init(_Left._Current >= _Right))) // strengthened - requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { + noexcept(_Fake_copy_init(_Left._Current >= _Right))) /* strengthened */ { return _Left._Current >= _Right; } - template <_Not_a_const_iterator _Other> + template <_Not_bci_order<_Iter> _Other> _NODISCARD_FRIEND constexpr bool operator<(const _Other& _Left, const basic_const_iterator& _Right) noexcept( - noexcept(_Fake_copy_init(_Left < _Right._Current))) // strengthened - requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { + noexcept(_Fake_copy_init(_Left < _Right._Current))) /* strengthened */ { return _Left < _Right._Current; } - template <_Not_a_const_iterator _Other> + template <_Not_bci_order<_Iter> _Other> _NODISCARD_FRIEND constexpr bool operator>(const _Other& _Left, const basic_const_iterator& _Right) noexcept( - noexcept(_Fake_copy_init(_Left > _Right._Current))) // strengthened - requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { + noexcept(_Fake_copy_init(_Left > _Right._Current))) /* strengthened */ { return _Left > _Right._Current; } - template <_Not_a_const_iterator _Other> + template <_Not_bci_order<_Iter> _Other> _NODISCARD_FRIEND constexpr bool operator<=(const _Other& _Left, const basic_const_iterator& _Right) noexcept( - noexcept(_Fake_copy_init(_Left <= _Right._Current))) // strengthened - requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { + noexcept(_Fake_copy_init(_Left <= _Right._Current))) /* strengthened */ { return _Left <= _Right._Current; } - template <_Not_a_const_iterator _Other> + template <_Not_bci_order<_Iter> _Other> _NODISCARD_FRIEND constexpr bool operator>=(const _Other& _Left, const basic_const_iterator& _Right) noexcept( - noexcept(_Fake_copy_init(_Left >= _Right._Current))) // strengthened - requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> { + noexcept(_Fake_copy_init(_Left >= _Right._Current))) /* strengthened */ { return _Left >= _Right._Current; } - template <_Not_same_as _Other> + template <_Bci_order_3way<_Iter> _Other> _NODISCARD_FRIEND constexpr auto operator<=>(const basic_const_iterator& _Left, const _Other& _Right) noexcept( - noexcept(_Left._Current <=> _Right)) // strengthened - requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> && // - three_way_comparable_with<_Iter, _Other> { + noexcept(_Left._Current <=> _Right)) /* strengthened */ { return _Left._Current <=> _Right; } From f1e813af12c82ab8e375b3c80e33e2d6903f8cec Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Fri, 16 Sep 2022 11:15:53 +0200 Subject: [PATCH 12/25] Cleanup const iterator machinery tests --- .../test.compile.pass.cpp | 35 ++++++++++++++----- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/tests/std/tests/P2278R4_ranges_const_iterator_machinery/test.compile.pass.cpp b/tests/std/tests/P2278R4_ranges_const_iterator_machinery/test.compile.pass.cpp index 0b532f84ea0..e0e196a5584 100644 --- a/tests/std/tests/P2278R4_ranges_const_iterator_machinery/test.compile.pass.cpp +++ b/tests/std/tests/P2278R4_ranges_const_iterator_machinery/test.compile.pass.cpp @@ -15,9 +15,16 @@ concept CanIterConstRef = requires { typename iter_const_reference_t; }; -template -concept CanConstIterator = requires { - typename const_iterator; +template +concept CanConstIterator = requires(It it) { + typename const_iterator; + { make_const_iterator(std::move(it)) } -> same_as>; +}; + +template +concept CanConstSentinel = requires(Se se) { + typename const_sentinel; + { make_const_sentinel(std::move(se)) } -> same_as>; }; static_assert(!CanIterConstRef); @@ -28,6 +35,7 @@ namespace test_pointer { using Ptr = int*; static_assert(CanIterConstRef); static_assert(CanConstIterator); + static_assert(CanConstSentinel); static_assert(same_as, const int&>); static_assert(same_as, basic_const_iterator>); static_assert(same_as>, const int&>); @@ -37,17 +45,18 @@ namespace test_pointer { using ConstPtr = const int*; static_assert(CanIterConstRef); static_assert(CanConstIterator); + static_assert(CanConstSentinel); static_assert(same_as, const int&>); static_assert(same_as, ConstPtr>); static_assert(same_as>, const int&>); static_assert(same_as, ConstPtr>); - static_assert(same_as>, const int&>); } // namespace test_pointer namespace test_random_access_iter { using Iter = deque::iterator; static_assert(CanIterConstRef); static_assert(CanConstIterator); + static_assert(CanConstSentinel); static_assert(same_as, const int&>); static_assert(same_as, basic_const_iterator>); static_assert(same_as>, const int&>); @@ -57,17 +66,18 @@ namespace test_random_access_iter { using ConstIter = deque::const_iterator; static_assert(CanIterConstRef); static_assert(CanConstIterator); + static_assert(CanConstSentinel); static_assert(same_as, const int&>); static_assert(same_as, ConstIter>); static_assert(same_as>, const int&>); static_assert(same_as, ConstIter>); - static_assert(same_as>, const int&>); } // namespace test_random_access_iter namespace test_bidirectional_iter { using Iter = list::iterator; static_assert(CanIterConstRef); static_assert(CanConstIterator); + static_assert(CanConstSentinel); static_assert(same_as, const int&>); static_assert(same_as, basic_const_iterator>); static_assert(same_as>, const int&>); @@ -77,17 +87,18 @@ namespace test_bidirectional_iter { using ConstIter = list::const_iterator; static_assert(CanIterConstRef); static_assert(CanConstIterator); + static_assert(CanConstSentinel); static_assert(same_as, const int&>); static_assert(same_as, ConstIter>); static_assert(same_as>, const int&>); static_assert(same_as, ConstIter>); - static_assert(same_as>, const int&>); } // namespace test_bidirectional_iter namespace test_forward_iter { using Iter = forward_list::iterator; static_assert(CanIterConstRef); static_assert(CanConstIterator); + static_assert(CanConstSentinel); static_assert(same_as, const int&>); static_assert(same_as, basic_const_iterator>); static_assert(same_as>, const int&>); @@ -97,17 +108,18 @@ namespace test_forward_iter { using ConstIter = forward_list::const_iterator; static_assert(CanIterConstRef); static_assert(CanConstIterator); + static_assert(CanConstSentinel); static_assert(same_as, const int&>); static_assert(same_as, ConstIter>); static_assert(same_as>, const int&>); static_assert(same_as, ConstIter>); - static_assert(same_as>, const int&>); } // namespace test_forward_iter namespace test_input_iter { using Iter = ranges::iterator_t>; static_assert(CanIterConstRef); static_assert(CanConstIterator); + static_assert(CanConstSentinel); static_assert(same_as, const int&>); static_assert(same_as, basic_const_iterator>); static_assert(same_as>, const int&>); @@ -121,6 +133,7 @@ namespace test_prvalue_iter { using Iter = ranges::iterator_t; static_assert(CanIterConstRef); static_assert(CanConstIterator); + static_assert(CanConstSentinel); static_assert(same_as, int>); static_assert(same_as, Iter>); static_assert(same_as>, int>); @@ -130,17 +143,18 @@ namespace test_prvalue_iter { using ConstIter = ranges::iterator_t; static_assert(CanIterConstRef); static_assert(CanConstIterator); + static_assert(CanConstSentinel); static_assert(same_as, int>); static_assert(same_as, ConstIter>); static_assert(same_as>, int>); static_assert(same_as, ConstIter>); - static_assert(same_as>, int>); } // namespace test_prvalue_iter namespace test_vector_bool_iter { using Iter = vector::iterator; static_assert(CanIterConstRef); static_assert(CanConstIterator); + static_assert(CanConstSentinel); static_assert(same_as, bool>); static_assert(same_as, basic_const_iterator>); static_assert(same_as>, bool>); @@ -150,19 +164,22 @@ namespace test_vector_bool_iter { using ConstIter = vector::const_iterator; static_assert(CanIterConstRef); static_assert(CanConstIterator); + static_assert(CanConstSentinel); static_assert(same_as, bool>); static_assert(same_as, ConstIter>); static_assert(same_as>, bool>); static_assert(same_as, ConstIter>); - static_assert(same_as>, bool>); } // namespace test_vector_bool_iter // Test standard sentinels static_assert(!CanIterConstRef); static_assert(!CanConstIterator); +static_assert(CanConstSentinel); static_assert(same_as, default_sentinel_t>); + static_assert(!CanIterConstRef); static_assert(!CanConstIterator); +static_assert(CanConstSentinel); static_assert(same_as, unreachable_sentinel_t>); int main() {} // COMPILE-ONLY From 5b63c7e2e4479eb298b584a565f1559fd62ba769 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Fri, 16 Sep 2022 11:30:11 +0200 Subject: [PATCH 13/25] Cleanup comments from `basic_const_iterator` test --- .../P2278R4_basic_const_iterator/test.cpp | 77 ++++++++----------- 1 file changed, 31 insertions(+), 46 deletions(-) diff --git a/tests/std/tests/P2278R4_basic_const_iterator/test.cpp b/tests/std/tests/P2278R4_basic_const_iterator/test.cpp index 2c0f98ca6fa..536143ee54d 100644 --- a/tests/std/tests/P2278R4_basic_const_iterator/test.cpp +++ b/tests/std/tests/P2278R4_basic_const_iterator/test.cpp @@ -44,8 +44,7 @@ constexpr void test_one(It iter) { return; } - // Validate basic_const_iterator::operator*() - { + { // Validate basic_const_iterator::operator*() same_as> decltype(auto) val = *citer; assert(val == *iter); static_assert(noexcept(*citer) == noexcept(static_cast>(*iter))); // strengthened @@ -64,8 +63,7 @@ constexpr void test_one(It iter) { } } - // Validate basic_const_iterator::operator++() - { + { // Validate basic_const_iterator::operator++() same_as decltype(auto) citer2 = ++citer; ++iter; assert(citer2 == iter); @@ -74,26 +72,24 @@ constexpr void test_one(It iter) { } // Validate basic_const_iterator::operator++(int) - { - if constexpr (forward_iterator) { - same_as decltype(auto) citer2 = citer++; - assert(citer2 == iter); - assert(*citer2 == *iter); - static_assert( - noexcept(citer++) == (noexcept(iter++) && is_nothrow_copy_constructible_v) ); // strengthened - } else { - static_assert(is_void_v); - static_assert(noexcept(citer++) == noexcept(iter++)); // strengthened - } - - iter++; - assert(citer == iter); - assert(*citer == *iter); + if constexpr (forward_iterator) { + same_as decltype(auto) citer2 = citer++; + assert(citer2 == iter); + assert(*citer2 == *iter); + static_assert( + noexcept(citer++) == (noexcept(iter++) && is_nothrow_copy_constructible_v) ); // strengthened + } else { + static_assert(is_void_v); + static_assert(noexcept(citer++) == noexcept(iter++)); // strengthened } + iter++; + assert(citer == iter); + assert(*citer == *iter); + + if constexpr (bidirectional_iterator) { - // Validate basic_const_iterator::operator--() - { + { // Validate basic_const_iterator::operator--() same_as decltype(auto) citer2 = --citer; --iter; assert(citer2 == iter); @@ -101,8 +97,7 @@ constexpr void test_one(It iter) { static_assert(noexcept(--citer) == noexcept(--iter)); // strengthened } - // Validate basic_const_iterator::operator--(int) - { + { // Validate basic_const_iterator::operator--(int) same_as decltype(auto) citer2 = citer--; assert(citer2 == iter); assert(*citer2 == *iter); @@ -116,8 +111,7 @@ constexpr void test_one(It iter) { } if constexpr (random_access_iterator) { - // Validate basic_const_iterator::operator+=() - { + { // Validate basic_const_iterator::operator+=() same_as decltype(auto) citer2 = (citer += 2); iter += 2; assert(citer2 == iter); @@ -125,8 +119,7 @@ constexpr void test_one(It iter) { static_assert(noexcept(citer += 2) == noexcept(iter += 2)); // strengthened } - // Validate basic_const_iterator::operator-=() - { + { // Validate basic_const_iterator::operator-=() same_as decltype(auto) citer2 = (citer -= 2); iter -= 2; assert(citer2 == iter); @@ -134,16 +127,14 @@ constexpr void test_one(It iter) { static_assert(noexcept(citer -= 2) == noexcept(iter -= 2)); // strengthened } - // Validate basic_const_iterator::operator[] - { + { // Validate basic_const_iterator::operator[] same_as> decltype(auto) val = citer[0]; assert(val == iter[0]); static_assert( noexcept(citer[0]) == noexcept(static_cast>(iter[0]))); // strengthened } - // Validate operator{<, >, <=, >=, <=>}(const basic_const_iterator&, const basic_const_iterator&) - { + { // Validate operator{<, >, <=, >=, <=>}(const basic_const_iterator&, const basic_const_iterator&) const same_as auto citer2 = (citer + 1); assert(citer < citer2); assert(citer2 > citer); @@ -160,8 +151,8 @@ constexpr void test_one(It iter) { iter += 2; // advance iter temporarily - // Validate operator{<, >, <=, >=, <=>}(const basic_const_iterator&, const "not same as basic_const_iterator"&) - { + { // Validate operator{<, >, <=, >=, <=>}(const basic_const_iterator&, const "not same as + // basic_const_iterator"&) assert(citer < iter); assert(!(citer > iter)); assert(citer <= iter); @@ -175,8 +166,7 @@ constexpr void test_one(It iter) { static_assert(noexcept(citer <=> iter) == noexcept(citer.base() <=> iter)); // strengthened } - // Validate operator{<, >, <=, >=}(const "not a const iterator"&, const basic_const_iterator&) - { + { // Validate operator{<, >, <=, >=}(const "not a const iterator"&, const basic_const_iterator&) assert(!(iter < citer)); assert(iter > citer); assert(!(iter <= citer)); @@ -188,8 +178,7 @@ constexpr void test_one(It iter) { static_assert(noexcept(iter >= citer) == noexcept(iter >= citer.base())); // strengthened } - // Validate operator+(const basic_const_iterator&, difference_type) - { + { // Validate operator+(const basic_const_iterator&, difference_type) const same_as auto citer2 = (citer + 2); const same_as auto citer3 = (2 + citer); assert(citer2 == citer3); @@ -199,8 +188,7 @@ constexpr void test_one(It iter) { static_assert(noexcept(2 + citer) == noexcept(2 + iter)); // strengthened } - // Validate operator-(const basic_const_iterator&, difference_type) - { + { // Validate operator-(const basic_const_iterator&, difference_type) citer += 4; const same_as auto citer2 = (citer - 2); assert(*citer2 == *iter); @@ -209,8 +197,7 @@ constexpr void test_one(It iter) { static_assert(noexcept(citer - 2) == noexcept(iter - 2)); // strengthened } - // Validate operator-(const basic_const_iterator&, sized_sentinel) - { + { // Validate operator-(const basic_const_iterator&, sized_sentinel) assert(citer - citer == 0); assert(citer - iter == -2); assert(iter - citer == 2); @@ -245,14 +232,12 @@ constexpr void test_one(It iter) { static_assert(noexcept(citer == sent) == noexcept(iter == sent)); // strengthened } - // Validate basic_const_iterator::base() const& - { + { // Validate basic_const_iterator::base() const& [[maybe_unused]] same_as decltype(auto) base = citer.base(); static_assert(noexcept(citer.base())); } - // Validate basic_const_iterator::base() && - { + { // Validate basic_const_iterator::base() && [[maybe_unused]] same_as decltype(auto) base = std::move(citer).base(); static_assert(noexcept(std::move(citer).base()) == is_nothrow_move_constructible_v); // strengthened } @@ -275,7 +260,7 @@ using test_iterator = test::iterator>(); instantiator::call>(); instantiator::call>(); From 85b0a2d43ca7a93f4b736dedcb13f76f62c411f4 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Fri, 16 Sep 2022 11:51:19 +0200 Subject: [PATCH 14/25] Test `common_type` too --- .../test.compile.pass.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/std/tests/P2278R4_ranges_const_iterator_machinery/test.compile.pass.cpp b/tests/std/tests/P2278R4_ranges_const_iterator_machinery/test.compile.pass.cpp index e0e196a5584..9cb447f1250 100644 --- a/tests/std/tests/P2278R4_ranges_const_iterator_machinery/test.compile.pass.cpp +++ b/tests/std/tests/P2278R4_ranges_const_iterator_machinery/test.compile.pass.cpp @@ -50,6 +50,12 @@ namespace test_pointer { static_assert(same_as, ConstPtr>); static_assert(same_as>, const int&>); static_assert(same_as, ConstPtr>); + + // Validate common_type + static_assert(same_as, ConstPtr>, basic_const_iterator>); + static_assert(same_as>, basic_const_iterator>); + static_assert(same_as, basic_const_iterator>, + basic_const_iterator>); } // namespace test_pointer namespace test_random_access_iter { From 5f2b19711e289fe7caaa90d174e309b499ac4bdf Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Tue, 20 Sep 2022 00:16:17 +0200 Subject: [PATCH 15/25] _EXPORT_STD! --- stl/inc/xutility | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index 6e330da942a..a833e571ed7 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -1681,16 +1681,16 @@ template concept _Not_same_as = !same_as, remove_cvref_t<_Ty2>>; #if _HAS_CXX23 -template +_EXPORT_STD template using iter_const_reference_t = common_reference_t&&, iter_reference_t<_Ty>>; template concept _Constant_iterator = input_iterator<_Ty> && same_as, iter_reference_t<_Ty>>; -template +_EXPORT_STD template class basic_const_iterator; -template +_EXPORT_STD template using const_iterator = conditional_t<_Constant_iterator<_Iter>, _Iter, basic_const_iterator<_Iter>>; template @@ -1703,7 +1703,7 @@ struct _Const_sentinel<_Sent> { using type = const_iterator<_Sent>; }; -template +_EXPORT_STD template using const_sentinel = typename _Const_sentinel<_Sent>::type; template @@ -1729,7 +1729,7 @@ template concept _Not_bci_order = _Not_a_const_iterator<_Ty> && random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Ty>; -template +_EXPORT_STD template class basic_const_iterator : public _Basic_const_iterator_category<_Iter> { private: /* [[no_unique_address]] */ _Iter _Current{}; @@ -1990,13 +1990,13 @@ struct common_type, basic_const_iterator<_Ty2>> { using type = basic_const_iterator>; }; -template +_EXPORT_STD template _NODISCARD constexpr const_iterator<_Iter> make_const_iterator(_Iter _It) noexcept( is_nothrow_constructible_v, _Iter>) /* strengthened */ { return _It; } -template +_EXPORT_STD template _NODISCARD constexpr const_sentinel<_Sent> make_const_sentinel(_Sent _Se) noexcept( is_nothrow_constructible_v, _Sent>) /* strengthened */ { return _Se; From fd9e81758c27f71c598586bd710386c24b17c96e Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Tue, 20 Sep 2022 12:25:48 +0200 Subject: [PATCH 16/25] `.base()` -> `._Current` --- stl/inc/xutility | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index a833e571ed7..076d115d14c 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -1969,9 +1969,9 @@ public: template <_Not_a_const_iterator _Sent> requires sized_sentinel_for<_Sent, _Iter> _NODISCARD_FRIEND constexpr difference_type operator-(const _Sent& _Se, const basic_const_iterator& _It) noexcept( - noexcept(_Se - _It.base())) /* strengthened */ { // per LWG-3769 + noexcept(_Se - _It._Current)) /* strengthened */ { // per LWG-3769 // clang-format on - return _Se - _It.base(); + return _Se - _It._Current; } }; From f9cb24aaac9657aef02ad12dfe72c085c78bfff7 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Sat, 15 Oct 2022 14:13:37 +0200 Subject: [PATCH 17/25] Update formatting --- stl/inc/xutility | 52 ++++++++++++------- .../P2278R4_basic_const_iterator/test.cpp | 4 +- .../test.compile.pass.cpp | 16 +++--- 3 files changed, 43 insertions(+), 29 deletions(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index 6b8f274e061..699030f8317 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -1717,8 +1717,10 @@ struct _Const_sentinel<_Sent> { _EXPORT_STD template using const_sentinel = typename _Const_sentinel<_Sent>::type; +// clang-format off template concept _Not_a_const_iterator = !_Is_specialization_v<_Ty, basic_const_iterator>; +// clang-format on template struct _Basic_const_iterator_category {}; @@ -1798,8 +1800,10 @@ public: } _NODISCARD constexpr const value_type* operator->() const - noexcept(contiguous_iterator<_Iter> || noexcept(*_Current)) /* strengthened */ requires - is_lvalue_reference_v> && same_as>, value_type> { + noexcept(contiguous_iterator<_Iter> || noexcept(*_Current)) /* strengthened */ + requires is_lvalue_reference_v> + && same_as>, value_type> + { if constexpr (contiguous_iterator<_Iter>) { return _STD to_address(_Current); } else { @@ -1818,21 +1822,24 @@ public: constexpr basic_const_iterator operator++(int) noexcept( noexcept(++*this) && is_nothrow_copy_constructible_v) // strengthened - requires forward_iterator<_Iter> { + requires forward_iterator<_Iter> + { auto _Tmp = *this; ++*this; return _Tmp; } constexpr basic_const_iterator& operator--() noexcept(noexcept(--_Current)) // strengthened - requires bidirectional_iterator<_Iter> { + requires bidirectional_iterator<_Iter> + { --_Current; return *this; } constexpr basic_const_iterator operator--(int) noexcept( noexcept(--*this) && is_nothrow_copy_constructible_v) // strengthened - requires bidirectional_iterator<_Iter> { + requires bidirectional_iterator<_Iter> + { auto _Tmp = *this; --*this; return _Tmp; @@ -1840,21 +1847,24 @@ public: constexpr basic_const_iterator& operator+=(const difference_type _Off) noexcept( noexcept(_Current += _Off)) // strengthened - requires random_access_iterator<_Iter> { + requires random_access_iterator<_Iter> + { _Current += _Off; return *this; } constexpr basic_const_iterator& operator-=(const difference_type _Off) noexcept( noexcept(_Current -= _Off)) // strengthened - requires random_access_iterator<_Iter> { + requires random_access_iterator<_Iter> + { _Current -= _Off; return *this; } _NODISCARD constexpr _Reference operator[](const difference_type _Idx) const noexcept(noexcept(static_cast<_Reference>(_Current[_Idx]))) // strengthened - requires random_access_iterator<_Iter> { + requires random_access_iterator<_Iter> + { return static_cast<_Reference>(_Current[_Idx]); } @@ -1867,34 +1877,39 @@ public: _NODISCARD_FRIEND constexpr bool operator<(const basic_const_iterator& _Left, const basic_const_iterator& _Right) noexcept( noexcept(_Fake_copy_init(_Left._Current < _Right._Current))) // strengthened - requires random_access_iterator<_Iter> { + requires random_access_iterator<_Iter> + { return _Left._Current < _Right._Current; } _NODISCARD_FRIEND constexpr bool operator>(const basic_const_iterator& _Left, const basic_const_iterator& _Right) noexcept( noexcept(_Fake_copy_init(_Left._Current > _Right._Current))) // strengthened - requires random_access_iterator<_Iter> { + requires random_access_iterator<_Iter> + { return _Left._Current > _Right._Current; } _NODISCARD_FRIEND constexpr bool operator<=(const basic_const_iterator& _Left, const basic_const_iterator& _Right) noexcept( noexcept(_Fake_copy_init(_Left._Current <= _Right._Current))) // strengthened - requires random_access_iterator<_Iter> { + requires random_access_iterator<_Iter> + { return _Left._Current <= _Right._Current; } _NODISCARD_FRIEND constexpr bool operator>=(const basic_const_iterator& _Left, const basic_const_iterator& _Right) noexcept( noexcept(_Fake_copy_init(_Left._Current >= _Right._Current))) // strengthened - requires random_access_iterator<_Iter> { + requires random_access_iterator<_Iter> + { return _Left._Current >= _Right._Current; } _NODISCARD_FRIEND constexpr auto operator<=>(const basic_const_iterator& _Left, const basic_const_iterator& _Right) noexcept(noexcept(_Left._Current <=> _Right._Current)) // strengthened - requires random_access_iterator<_Iter> && three_way_comparable<_Iter> { + requires random_access_iterator<_Iter> && three_way_comparable<_Iter> + { return _Left._Current <=> _Right._Current; } @@ -1954,19 +1969,22 @@ public: _NODISCARD_FRIEND constexpr basic_const_iterator operator+(const basic_const_iterator& _It, const difference_type _Off) noexcept(noexcept(basic_const_iterator{_It._Current + _Off})) // strengthened - requires random_access_iterator<_Iter> { + requires random_access_iterator<_Iter> + { return basic_const_iterator{_It._Current + _Off}; } _NODISCARD_FRIEND constexpr basic_const_iterator operator+(const difference_type _Off, const basic_const_iterator& _It) noexcept(noexcept(basic_const_iterator{_It._Current + _Off})) // strengthened - requires random_access_iterator<_Iter> { + requires random_access_iterator<_Iter> + { return basic_const_iterator{_It._Current + _Off}; } _NODISCARD_FRIEND constexpr basic_const_iterator operator-(const basic_const_iterator& _It, const difference_type _Off) noexcept(noexcept(basic_const_iterator{_It._Current - _Off})) // strengthened - requires random_access_iterator<_Iter> { + requires random_access_iterator<_Iter> + { return basic_const_iterator{_It._Current - _Off}; } @@ -1976,12 +1994,10 @@ public: return _Current - _Se; } - // clang-format off template <_Not_a_const_iterator _Sent> requires sized_sentinel_for<_Sent, _Iter> _NODISCARD_FRIEND constexpr difference_type operator-(const _Sent& _Se, const basic_const_iterator& _It) noexcept( noexcept(_Se - _It._Current)) /* strengthened */ { // per LWG-3769 - // clang-format on return _Se - _It._Current; } }; diff --git a/tests/std/tests/P2278R4_basic_const_iterator/test.cpp b/tests/std/tests/P2278R4_basic_const_iterator/test.cpp index 536143ee54d..c3463483e30 100644 --- a/tests/std/tests/P2278R4_basic_const_iterator/test.cpp +++ b/tests/std/tests/P2278R4_basic_const_iterator/test.cpp @@ -13,8 +13,8 @@ using namespace std; template concept HasPeek = requires(It iter) { - { iter.peek() } -> same_as>>; -}; + { iter.peek() } -> same_as>>; + }; template constexpr void test_one(It iter) { diff --git a/tests/std/tests/P2278R4_ranges_const_iterator_machinery/test.compile.pass.cpp b/tests/std/tests/P2278R4_ranges_const_iterator_machinery/test.compile.pass.cpp index 9cb447f1250..32ed54f22c4 100644 --- a/tests/std/tests/P2278R4_ranges_const_iterator_machinery/test.compile.pass.cpp +++ b/tests/std/tests/P2278R4_ranges_const_iterator_machinery/test.compile.pass.cpp @@ -11,21 +11,19 @@ using namespace std; template -concept CanIterConstRef = requires { - typename iter_const_reference_t; -}; +concept CanIterConstRef = requires { typename iter_const_reference_t; }; template concept CanConstIterator = requires(It it) { - typename const_iterator; - { make_const_iterator(std::move(it)) } -> same_as>; -}; + typename const_iterator; + { make_const_iterator(std::move(it)) } -> same_as>; + }; template concept CanConstSentinel = requires(Se se) { - typename const_sentinel; - { make_const_sentinel(std::move(se)) } -> same_as>; -}; + typename const_sentinel; + { make_const_sentinel(std::move(se)) } -> same_as>; + }; static_assert(!CanIterConstRef); static_assert(!CanIterConstRef>); From ed53e6bd4e62a2109c3c51b53a983331b9aaab7f Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Thu, 20 Oct 2022 23:31:56 +0200 Subject: [PATCH 18/25] Implement LWG-3765 --- stl/inc/xutility | 4 ++-- .../test.compile.pass.cpp | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index 699030f8317..2ab4a5999b2 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -1714,7 +1714,7 @@ struct _Const_sentinel<_Sent> { using type = const_iterator<_Sent>; }; -_EXPORT_STD template +_EXPORT_STD template using const_sentinel = typename _Const_sentinel<_Sent>::type; // clang-format off @@ -2023,7 +2023,7 @@ _NODISCARD constexpr const_iterator<_Iter> make_const_iterator(_Iter _It) noexce return _It; } -_EXPORT_STD template +_EXPORT_STD template _NODISCARD constexpr const_sentinel<_Sent> make_const_sentinel(_Sent _Se) noexcept( is_nothrow_constructible_v, _Sent>) /* strengthened */ { return _Se; diff --git a/tests/std/tests/P2278R4_ranges_const_iterator_machinery/test.compile.pass.cpp b/tests/std/tests/P2278R4_ranges_const_iterator_machinery/test.compile.pass.cpp index 32ed54f22c4..aeafe42e108 100644 --- a/tests/std/tests/P2278R4_ranges_const_iterator_machinery/test.compile.pass.cpp +++ b/tests/std/tests/P2278R4_ranges_const_iterator_machinery/test.compile.pass.cpp @@ -186,4 +186,11 @@ static_assert(!CanConstIterator); static_assert(CanConstSentinel); static_assert(same_as, unreachable_sentinel_t>); +struct NotSemiregular { + NotSemiregular() = default; + NotSemiregular(const NotSemiregular&) = delete; +}; + +static_assert(!CanConstSentinel); + int main() {} // COMPILE-ONLY From 94828be11728b2c7eea10cc223a852a957598379 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 20 Oct 2022 11:44:33 -0700 Subject: [PATCH 19/25] Say TRANSITION in the compiler bug workaround comment. --- stl/inc/xutility | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index 2ab4a5999b2..52c72be9141 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -1730,7 +1730,7 @@ struct _Basic_const_iterator_category<_Iter> { using iterator_category = typename iterator_traits<_Iter>::iterator_category; }; -// These are distinct concepts to workaround LLVM-55945 +// TRANSITION, LLVM-55945: These are distinct concepts as a workaround template concept _Bci_order = _Not_same_as<_Ty, basic_const_iterator<_Iter>> && random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Ty>; From 6f5eb310eaab8a1c37fbd2af783b9bee1bebedb5 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 20 Oct 2022 13:34:33 -0700 Subject: [PATCH 20/25] Drop std:: qualification. --- .../std/tests/P2278R4_basic_const_iterator/test.cpp | 12 ++++++------ .../test.compile.pass.cpp | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/std/tests/P2278R4_basic_const_iterator/test.cpp b/tests/std/tests/P2278R4_basic_const_iterator/test.cpp index c3463483e30..485164c4440 100644 --- a/tests/std/tests/P2278R4_basic_const_iterator/test.cpp +++ b/tests/std/tests/P2278R4_basic_const_iterator/test.cpp @@ -54,12 +54,12 @@ constexpr void test_one(It iter) { if constexpr (is_lvalue_reference_v> && // same_as>, iter_value_t>) { const same_as*> auto ptr = citer.operator->(); - assert(ptr == std::addressof(*citer)); + assert(ptr == addressof(*citer)); if constexpr (contiguous_iterator) { - static_assert(noexcept(citer.operator->()) == noexcept(std::to_address(iter))); // strengthened + static_assert(noexcept(citer.operator->()) == noexcept(to_address(iter))); // strengthened } else { - static_assert(noexcept(citer.operator->()) == noexcept(std::addressof(*iter))); // strengthened + static_assert(noexcept(citer.operator->()) == noexcept(addressof(*iter))); // strengthened } } @@ -219,7 +219,7 @@ constexpr void test_one(It iter) { // Validate to_address if constexpr (contiguous_iterator && HasPeek) { - assert(std::to_address(citer) == iter.peek()); + assert(to_address(citer) == iter.peek()); } // Validate basic_const_iterator::operator==() @@ -238,8 +238,8 @@ constexpr void test_one(It iter) { } { // Validate basic_const_iterator::base() && - [[maybe_unused]] same_as decltype(auto) base = std::move(citer).base(); - static_assert(noexcept(std::move(citer).base()) == is_nothrow_move_constructible_v); // strengthened + [[maybe_unused]] same_as decltype(auto) base = move(citer).base(); + static_assert(noexcept(move(citer).base()) == is_nothrow_move_constructible_v); // strengthened } } diff --git a/tests/std/tests/P2278R4_ranges_const_iterator_machinery/test.compile.pass.cpp b/tests/std/tests/P2278R4_ranges_const_iterator_machinery/test.compile.pass.cpp index aeafe42e108..a489a44effa 100644 --- a/tests/std/tests/P2278R4_ranges_const_iterator_machinery/test.compile.pass.cpp +++ b/tests/std/tests/P2278R4_ranges_const_iterator_machinery/test.compile.pass.cpp @@ -16,13 +16,13 @@ concept CanIterConstRef = requires { typename iter_const_reference_t; }; template concept CanConstIterator = requires(It it) { typename const_iterator; - { make_const_iterator(std::move(it)) } -> same_as>; + { make_const_iterator(move(it)) } -> same_as>; }; template concept CanConstSentinel = requires(Se se) { typename const_sentinel; - { make_const_sentinel(std::move(se)) } -> same_as>; + { make_const_sentinel(move(se)) } -> same_as>; }; static_assert(!CanIterConstRef); From 65c2793bf844b148645e031373c653a2991eeacb Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 20 Oct 2022 14:59:01 -0700 Subject: [PATCH 21/25] Drop forced wrapping - clang-format 15 is ok. --- tests/std/tests/P2278R4_basic_const_iterator/test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/std/tests/P2278R4_basic_const_iterator/test.cpp b/tests/std/tests/P2278R4_basic_const_iterator/test.cpp index 485164c4440..5e834a985a7 100644 --- a/tests/std/tests/P2278R4_basic_const_iterator/test.cpp +++ b/tests/std/tests/P2278R4_basic_const_iterator/test.cpp @@ -51,8 +51,8 @@ constexpr void test_one(It iter) { } // Validate basic_const_iterator::operator->() - if constexpr (is_lvalue_reference_v> && // - same_as>, iter_value_t>) { + if constexpr (is_lvalue_reference_v> + && same_as>, iter_value_t>) { const same_as*> auto ptr = citer.operator->(); assert(ptr == addressof(*citer)); From ab3ae631c0380a0842ccf39c7a3117725c9207cc Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 20 Oct 2022 15:04:50 -0700 Subject: [PATCH 22/25] Include more headers. --- .../test.compile.pass.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/std/tests/P2278R4_ranges_const_iterator_machinery/test.compile.pass.cpp b/tests/std/tests/P2278R4_ranges_const_iterator_machinery/test.compile.pass.cpp index a489a44effa..157929c6073 100644 --- a/tests/std/tests/P2278R4_ranges_const_iterator_machinery/test.compile.pass.cpp +++ b/tests/std/tests/P2278R4_ranges_const_iterator_machinery/test.compile.pass.cpp @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include using namespace std; From e370870d3043e7da88059e350c2f8fe9aa089007 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 20 Oct 2022 15:30:12 -0700 Subject: [PATCH 23/25] Fix test: istream_view's iterator isn't semiregular, so it can't support const_sentinel. --- .../test.compile.pass.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/std/tests/P2278R4_ranges_const_iterator_machinery/test.compile.pass.cpp b/tests/std/tests/P2278R4_ranges_const_iterator_machinery/test.compile.pass.cpp index 157929c6073..9a33ffe39d4 100644 --- a/tests/std/tests/P2278R4_ranges_const_iterator_machinery/test.compile.pass.cpp +++ b/tests/std/tests/P2278R4_ranges_const_iterator_machinery/test.compile.pass.cpp @@ -125,12 +125,10 @@ namespace test_input_iter { using Iter = ranges::iterator_t>; static_assert(CanIterConstRef); static_assert(CanConstIterator); - static_assert(CanConstSentinel); + static_assert(!CanConstSentinel); static_assert(same_as, const int&>); static_assert(same_as, basic_const_iterator>); static_assert(same_as>, const int&>); - static_assert(same_as, basic_const_iterator>); - static_assert(same_as>, const int&>); } // namespace test_input_iter namespace test_prvalue_iter { From 2872f2279f5cff1884462da56ea6a9e786eb1b9e Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 20 Oct 2022 15:36:23 -0700 Subject: [PATCH 24/25] make_const_meow() constructs from lvalues. --- stl/inc/xutility | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index 52c72be9141..59150ecce96 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -2019,13 +2019,13 @@ struct common_type, basic_const_iterator<_Ty2>> { _EXPORT_STD template _NODISCARD constexpr const_iterator<_Iter> make_const_iterator(_Iter _It) noexcept( - is_nothrow_constructible_v, _Iter>) /* strengthened */ { + is_nothrow_constructible_v, _Iter&>) /* strengthened */ { return _It; } _EXPORT_STD template _NODISCARD constexpr const_sentinel<_Sent> make_const_sentinel(_Sent _Se) noexcept( - is_nothrow_constructible_v, _Sent>) /* strengthened */ { + is_nothrow_constructible_v, _Sent&>) /* strengthened */ { return _Se; } #endif // _HAS_CXX23 From 00e92998398e76cc9d7ff469ec8665ecc361d5b4 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Fri, 21 Oct 2022 02:44:52 +0200 Subject: [PATCH 25/25] Fix test coverage --- .../std/tests/P2278R4_basic_const_iterator/test.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/std/tests/P2278R4_basic_const_iterator/test.cpp b/tests/std/tests/P2278R4_basic_const_iterator/test.cpp index 5e834a985a7..b08f706aa6e 100644 --- a/tests/std/tests/P2278R4_basic_const_iterator/test.cpp +++ b/tests/std/tests/P2278R4_basic_const_iterator/test.cpp @@ -11,11 +11,19 @@ using namespace std; +template +concept Pointer = is_pointer_v; + template -concept HasPeek = requires(It iter) { - { iter.peek() } -> same_as>>; +concept HasPeek = requires(const It& iter) { + { iter.peek() } -> Pointer; + requires convertible_to*>; }; +static_assert(!HasPeek); +static_assert(!HasPeek>); +static_assert(HasPeek>); + template constexpr void test_one(It iter) { using ConstIt = basic_const_iterator;