diff --git a/stl/inc/ranges b/stl/inc/ranges index f5d0ff9b36c..9e07a4743b7 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -1328,9 +1328,7 @@ namespace ranges { #endif // _ITERATOR_DEBUG_LEVEL != 0 } - _NODISCARD constexpr iterator_t<_Vw> base() const& noexcept( - is_nothrow_copy_constructible_v>) // strengthened - requires copyable> { + _NODISCARD constexpr const iterator_t<_Vw>& base() const& noexcept /* strengthened */ { return _Current; } _NODISCARD constexpr iterator_t<_Vw> base() && noexcept( @@ -1640,9 +1638,7 @@ namespace ranges { : _Current{_STD move(_It._Current)}, _Parent{_It._Parent} {} // clang-format on - _NODISCARD constexpr iterator_t<_Base> base() const& noexcept( - is_nothrow_copy_constructible_v>) // strengthened - requires copyable> { + _NODISCARD constexpr const iterator_t<_Base>& base() const& noexcept /* strengthened */ { return _Current; } _NODISCARD constexpr iterator_t<_Base> base() && noexcept( @@ -4064,9 +4060,7 @@ namespace ranges { : _Current{_STD move(_It._Current)} {} // clang-format on - _NODISCARD constexpr iterator_t<_Base> base() const& noexcept( - is_nothrow_copy_constructible_v>) /* strengthened */ - requires copyable> { + _NODISCARD constexpr const iterator_t<_Base>& base() const& noexcept /* strengthened */ { return _Current; } diff --git a/tests/std/test.lst b/tests/std/test.lst index 38896e23e0c..d4359f9419b 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -385,6 +385,7 @@ tests\P0896R4_views_elements tests\P0896R4_views_empty tests\P0896R4_views_filter tests\P0896R4_views_filter_death +tests\P0896R4_views_filter_iterator tests\P0896R4_views_iota tests\P0896R4_views_join tests\P0896R4_views_reverse diff --git a/tests/std/tests/P0896R4_views_filter_iterator/env.lst b/tests/std/tests/P0896R4_views_filter_iterator/env.lst new file mode 100644 index 00000000000..8ac7033b206 --- /dev/null +++ b/tests/std/tests/P0896R4_views_filter_iterator/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\strict_concepts_latest_matrix.lst diff --git a/tests/std/tests/P0896R4_views_filter_iterator/test.cpp b/tests/std/tests/P0896R4_views_filter_iterator/test.cpp new file mode 100644 index 00000000000..7098949ada5 --- /dev/null +++ b/tests/std/tests/P0896R4_views_filter_iterator/test.cpp @@ -0,0 +1,187 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include + +#include +using namespace std; + +constexpr auto is_even = [](const auto& x) { return x % 2 == 0; }; +using Pred = remove_const_t; + +struct iterator_instantiator { + template + static constexpr void call() { + // Pre: Iter is a specialization of test::iterator whose element type is const int + int mutable_ints[] = {0, 1, 2, 3, 4, 5, 6, 7}; + const auto make_view = [&] { + return views::filter( + ranges::subrange{Iter{mutable_ints}, test::sentinel{ranges::end(mutable_ints)}}, is_even); + }; + using R = decltype(make_view()); + using I = ranges::iterator_t; + using S = ranges::sentinel_t; + + // Validate nested types + static_assert(is_same_v, bidirectional_iterator_tag, + conditional_t, forward_iterator_tag, input_iterator_tag>>>); + + using C = typename iterator_traits::iterator_category; + static_assert(is_same_v, bidirectional_iterator_tag, + conditional_t, forward_iterator_tag, input_iterator_tag>>>); + + { // Validate iterator special member functions and base + static_assert(default_initializable == default_initializable); + if constexpr (default_initializable) { + I defaultConstructed{}; + assert(move(defaultConstructed).base().peek() == nullptr); + static_assert(is_nothrow_default_constructible_v); + } + + auto r0 = make_view(); + I valueConstructed{r0, Iter{mutable_ints}}; + static_assert(is_nothrow_constructible_v); + + if constexpr (copyable) { + I copyConstructed{valueConstructed}; + assert(copyConstructed == valueConstructed); + static_assert(is_nothrow_copy_constructible_v); + + auto r1 = make_view(); + I copyAssigned{r1, Iter{mutable_ints + 8}}; + copyAssigned = copyConstructed; + assert(copyAssigned == valueConstructed); + static_assert(is_nothrow_copy_assignable_v); + static_assert(same_as); + } + assert(as_const(valueConstructed).base().peek() == mutable_ints); + assert(move(valueConstructed).base().peek() == mutable_ints); + static_assert(same_as); + } + + { // Validate sentinel constructors and base + S defaultConstructed{}; + assert(defaultConstructed.base().peek() == nullptr); + static_assert(is_nothrow_default_constructible_v); + + auto r0 = make_view(); + S valueConstructed{r0}; + assert(valueConstructed.base().peek() == end(mutable_ints)); + + S copyConstructed{valueConstructed}; + assert(copyConstructed.base().peek() == valueConstructed.base().peek()); + static_assert(is_nothrow_copy_constructible_v); + + defaultConstructed = copyConstructed; + assert(defaultConstructed.base().peek() == valueConstructed.base().peek()); + static_assert(is_nothrow_copy_assignable_v); + } + + { // Validate dereference ops + auto r0 = make_view(); + auto i0 = r0.begin(); + assert(*i0 == 0); + static_assert(noexcept(*i0)); + + assert(ranges::iter_move(i0) == 0); // NB: moving from int leaves it unchanged + static_assert(noexcept(ranges::iter_move(i0)) == noexcept(ranges::iter_move(declval()))); + + if constexpr (forward_iterator) { + auto i1 = ranges::next(i0); + ranges::iter_swap(i0, i1); + assert(mutable_ints[0] == 2); + assert(mutable_ints[2] == 0); + ranges::iter_swap(i1, i0); + assert(mutable_ints[0] == 0); + assert(mutable_ints[2] == 2); + static_assert(noexcept(ranges::iter_swap(i0, i1))); + } + } + + { // Validate increments + auto r0 = make_view(); + auto i0 = r0.begin(); + assert(&++i0 == &i0); + assert(move(i0).base().peek() == mutable_ints + 2); + + auto r1 = make_view(); + auto i1 = r1.begin(); + if constexpr (forward_iterator) { + assert(i1++ == r1.begin()); + } else { + i1++; + } + assert(move(i1).base().peek() == mutable_ints + 2); + } + + if constexpr (bidirectional_iterator) { // Validate decrements + auto r = make_view(); + const auto second = ranges::next(r.begin()); + auto i = second; + assert(&--i == &i); + assert(i.base().peek() == mutable_ints); + + i = second; + assert(i-- == second); + assert(i.base().peek() == mutable_ints); + } + + if constexpr (equality_comparable) { + // Validate == and != + auto r = make_view(); + const auto first = r.begin(); + const auto last = r.end(); + + assert(first == first); + assert(I{} == I{}); + + assert(!(first == last)); + assert(!(last == first)); + + assert(!(first != first)); + assert(!(I{} != I{})); + + if constexpr (forward_iterator) { + const auto final = ranges::next(first, last); + assert(!(first == final)); + assert(first != final); + + assert(last == final); + assert(final == last); + + assert(!(last != final)); + assert(!(final != last)); + } + } + } +}; + +template +using test_iterator = + test::iterator}, + test::ProxyRef{!derived_from}>; + +constexpr void iterator_instantiation_test() { + using test::CanDifference; + + iterator_instantiator::call>(); + + iterator_instantiator::call>(); + iterator_instantiator::call>(); + + iterator_instantiator::call>(); + iterator_instantiator::call>(); + + iterator_instantiator::call>(); + iterator_instantiator::call>(); +} + +int main() { + static_assert((iterator_instantiation_test(), true)); + iterator_instantiation_test(); +} diff --git a/tests/std/tests/P0896R4_views_transform/test.cpp b/tests/std/tests/P0896R4_views_transform/test.cpp index ec8ee4e15ff..a7b116d4bba 100644 --- a/tests/std/tests/P0896R4_views_transform/test.cpp +++ b/tests/std/tests/P0896R4_views_transform/test.cpp @@ -242,14 +242,14 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { STATIC_ASSERT(CanEnd == (range && const_invocable)); if (!is_empty) { same_as> auto s = r.end(); - static_assert(is_same_v, iterator_t> == common_range); + STATIC_ASSERT(is_same_v, iterator_t> == common_range); if constexpr (bidirectional_range && common_range) { assert(*prev(s) == *prev(end(expected))); } if constexpr (CanEnd) { same_as> auto sc = as_const(r).end(); - static_assert(is_same_v, iterator_t> == common_range); + STATIC_ASSERT(is_same_v, iterator_t> == common_range); if constexpr (bidirectional_range && common_range) { assert(*prev(sc) == *prev(end(expected))); } @@ -457,8 +457,11 @@ struct iterator_instantiator { copyAssigned = copyConstructed; assert(copyAssigned == valueConstructed); STATIC_ASSERT(is_nothrow_copy_assignable_v); + STATIC_ASSERT(same_as); } + assert(as_const(valueConstructed).base().peek() == mutable_ints); assert(move(valueConstructed).base().peek() == mutable_ints); + STATIC_ASSERT(same_as); if constexpr (forward_iterator) { auto r1 = make_view();