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]} diff --git a/stl/inc/xutility b/stl/inc/xutility index ba8ed1e070b..59150ecce96 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -1691,6 +1691,345 @@ _NODISCARD constexpr const _Elem* data(initializer_list<_Elem> _Ilist) noexcept template concept _Not_same_as = (!same_as, remove_cvref_t<_Ty2>>); +#if _HAS_CXX23 +_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>>; + +_EXPORT_STD template +class basic_const_iterator; + +_EXPORT_STD 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>; +}; + +_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 {}; + +template +struct _Basic_const_iterator_category<_Iter> { + using iterator_category = typename iterator_traits<_Iter>::iterator_category; +}; + +// 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>; + +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>; + +_EXPORT_STD 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 constexpr bool operator==(const _Sent& _Se) const // per LWG-3769 + noexcept(noexcept(_Fake_copy_init(_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 + 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> + { + 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> + { + 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> + { + 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 <_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 */ { + return _Left._Current < _Right; + } + + 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 */ { + return _Left._Current > _Right; + } + + 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 */ { + return _Left._Current <= _Right; + } + + 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 */ { + return _Left._Current >= _Right; + } + + 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 */ { + return _Left < _Right._Current; + } + + 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 */ { + return _Left > _Right._Current; + } + + 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 */ { + return _Left <= _Right._Current; + } + + 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 */ { + return _Left >= _Right._Current; + } + + 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 */ { + 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 + 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 constexpr difference_type operator-(const _Sent& _Se) const // per LWG-3769 + noexcept(noexcept(_Current - _Se)) /* strengthened */ { + return _Current - _Se; + } + + 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 + 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>; +}; + +_EXPORT_STD template +_NODISCARD constexpr const_iterator<_Iter> make_const_iterator(_Iter _It) noexcept( + 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 */ { + 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 7e7c78319ff..cd8749a4571 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -322,6 +322,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) // P2291R3 constexpr Integral // P2302R4 ranges::contains, ranges::contains_subrange // P2321R2 zip diff --git a/tests/std/include/range_algorithm_support.hpp b/tests/std/include/range_algorithm_support.hpp index 1f88f13a11d..1c4c0db2ce6 100644 --- a/tests/std/include/range_algorithm_support.hpp +++ b/tests/std/include/range_algorithm_support.hpp @@ -361,7 +361,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}, diff --git a/tests/std/test.lst b/tests/std/test.lst index 24d9ded681f..2501eeab87c 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -545,6 +545,8 @@ 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 tests\P2321R2_proxy_reference 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..b08f706aa6e --- /dev/null +++ b/tests/std/tests/P2278R4_basic_const_iterator/test.cpp @@ -0,0 +1,288 @@ +// 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 Pointer = is_pointer_v; + +template +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; + 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 == addressof(*citer)); + + if constexpr (contiguous_iterator) { + static_assert(noexcept(citer.operator->()) == noexcept(to_address(iter))); // strengthened + } else { + static_assert(noexcept(citer.operator->()) == noexcept(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(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 = move(citer).base(); + static_assert(noexcept(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, but oblivious to proxyness + 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(); +} 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..9a33ffe39d4 --- /dev/null +++ b/tests/std/tests/P2278R4_ranges_const_iterator_machinery/test.compile.pass.cpp @@ -0,0 +1,196 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +template +concept CanIterConstRef = requires { typename iter_const_reference_t; }; + +template +concept CanConstIterator = requires(It it) { + typename const_iterator; + { make_const_iterator(move(it)) } -> same_as>; + }; + +template +concept CanConstSentinel = requires(Se se) { + typename const_sentinel; + { make_const_sentinel(move(se)) } -> same_as>; + }; + +static_assert(!CanIterConstRef); +static_assert(!CanIterConstRef>); +static_assert(!CanIterConstRef>); + +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&>); + 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(CanConstSentinel); + static_assert(same_as, const int&>); + 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 { + 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&>); + 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(CanConstSentinel); + static_assert(same_as, const int&>); + static_assert(same_as, ConstIter>); + static_assert(same_as>, const int&>); + static_assert(same_as, ConstIter>); +} // 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&>); + 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(CanConstSentinel); + static_assert(same_as, const int&>); + static_assert(same_as, ConstIter>); + static_assert(same_as>, const int&>); + static_assert(same_as, ConstIter>); +} // 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&>); + 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(CanConstSentinel); + static_assert(same_as, const int&>); + static_assert(same_as, ConstIter>); + static_assert(same_as>, const int&>); + static_assert(same_as, ConstIter>); +} // 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&>); +} // 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(CanConstSentinel); + 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(CanConstSentinel); + static_assert(same_as, int>); + static_assert(same_as, ConstIter>); + static_assert(same_as>, int>); + static_assert(same_as, ConstIter>); +} // 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>); + 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(CanConstSentinel); + static_assert(same_as, bool>); + static_assert(same_as, ConstIter>); + static_assert(same_as>, bool>); + static_assert(same_as, ConstIter>); +} // 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>); + +struct NotSemiregular { + NotSemiregular() = default; + NotSemiregular(const NotSemiregular&) = delete; +}; + +static_assert(!CanConstSentinel); + +int main() {} // COMPILE-ONLY