Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adopt LWG-3533 Make base() const & consistent across iterator wrappers that support input_iterators #1993

Merged
merged 8 commits into from
Jul 30, 2021
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 3 additions & 7 deletions stl/inc/ranges
Original file line number Diff line number Diff line change
Expand Up @@ -1301,8 +1301,7 @@ namespace ranges {
#endif // _ITERATOR_DEBUG_LEVEL != 0
}

_NODISCARD constexpr iterator_t<_Vw> base() const& noexcept(is_nothrow_copy_constructible_v<
iterator_t<_Vw>>) /* strengthened */ requires copyable<iterator_t<_Vw>> {
_NODISCARD constexpr const iterator_t<_Vw>& base() const& noexcept /* strengthened */ {
return _Current;
}
_NODISCARD constexpr iterator_t<_Vw> base() && noexcept(
Expand Down Expand Up @@ -1610,8 +1609,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<
iterator_t<_Base>>) /* strengthened */ requires copyable<iterator_t<_Base>> {
_NODISCARD constexpr const iterator_t<_Base>& base() const& /* strengthened */ {
StephanTLavavej marked this conversation as resolved.
Show resolved Hide resolved
return _Current;
}
_NODISCARD constexpr iterator_t<_Base> base() && noexcept(
Expand Down Expand Up @@ -4021,9 +4019,7 @@ namespace ranges {
: _Current{_STD move(_It._Current)} {}
// clang-format on

_NODISCARD constexpr iterator_t<_Base> base() const& noexcept(
is_nothrow_copy_constructible_v<iterator_t<_Base>>) /* strengthened */
requires copyable<iterator_t<_Base>> {
_NODISCARD constexpr const iterator_t<_Base>& base() const& /* strengthened */ {
return _Current;
}

Expand Down
1 change: 1 addition & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions tests/std/tests/P0896R4_views_filter_iterator/env.lst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

RUNALL_INCLUDE ..\strict_concepts_latest_matrix.lst
miscco marked this conversation as resolved.
Show resolved Hide resolved
187 changes: 187 additions & 0 deletions tests/std/tests/P0896R4_views_filter_iterator/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <cassert>
#include <ranges>
#include <type_traits>
#include <utility>

#include <range_algorithm_support.hpp>
using namespace std;

constexpr auto is_even = [](const auto& x) { return x % 2 == 0; };
miscco marked this conversation as resolved.
Show resolved Hide resolved
using Pred = remove_const_t<decltype(is_even)>;

struct iterator_instantiator {
template <input_iterator Iter>
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<int>{ranges::end(mutable_ints)}}, is_even);
};
using R = decltype(make_view());
using I = ranges::iterator_t<R>;
using S = ranges::sentinel_t<R>;

// Validate nested types
static_assert(is_same_v<typename I::iterator_concept,
conditional_t<bidirectional_iterator<Iter>, bidirectional_iterator_tag,
conditional_t<forward_iterator<Iter>, forward_iterator_tag, input_iterator_tag>>>);

using C = typename iterator_traits<Iter>::iterator_category;
static_assert(is_same_v<typename I::iterator_category,
conditional_t<derived_from<C, bidirectional_iterator_tag>, bidirectional_iterator_tag,
conditional_t<derived_from<C, forward_iterator_tag>, forward_iterator_tag, input_iterator_tag>>>);

{ // Validate iterator special member functions and base
static_assert(default_initializable<I> == default_initializable<Iter>);
if constexpr (default_initializable<Iter>) {
I defaultConstructed{};
assert(move(defaultConstructed).base().peek() == nullptr);
static_assert(is_nothrow_default_constructible_v<I>);
}

auto r0 = make_view();
I valueConstructed{r0, Iter{mutable_ints}};
static_assert(is_nothrow_constructible_v<I, R&, Iter>);

if constexpr (copyable<Iter>) {
I copyConstructed{valueConstructed};
assert(copyConstructed == valueConstructed);
static_assert(is_nothrow_copy_constructible_v<I>);

auto r1 = make_view();
I copyAssigned{r1, Iter{mutable_ints + 8}};
copyAssigned = copyConstructed;
assert(copyAssigned == valueConstructed);
static_assert(is_nothrow_copy_assignable_v<I>);
static_assert(same_as<const Iter&, decltype(as_const(copyConstructed).base())>);
}
assert(as_const(valueConstructed).base().peek() == mutable_ints);
assert(move(valueConstructed).base().peek() == mutable_ints);
static_assert(same_as<Iter, decltype(move(valueConstructed).base())>);
}

{ // Validate sentinel constructors and base
S defaultConstructed{};
assert(defaultConstructed.base().peek() == nullptr);
static_assert(is_nothrow_default_constructible_v<S>);

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<S>);

defaultConstructed = copyConstructed;
assert(defaultConstructed.base().peek() == valueConstructed.base().peek());
static_assert(is_nothrow_copy_assignable_v<S>);
}

{ // 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)));
CaseyCarter marked this conversation as resolved.
Show resolved Hide resolved

if constexpr (forward_iterator<Iter>) {
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<Iter>) {
assert(i1++ == r1.begin());
} else {
i1++;
}
assert(move(i1).base().peek() == mutable_ints + 2);
}

if constexpr (bidirectional_iterator<Iter>) { // 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<Iter>) {
// 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<Iter>) {
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 <class Category, test::CanDifference Diff>
using test_iterator =
test::iterator<Category, int, Diff, test::CanCompare{derived_from<Category, forward_iterator_tag>},
test::ProxyRef{!derived_from<Category, contiguous_iterator_tag>}>;

constexpr void iterator_instantiation_test() {
using test::CanDifference;

iterator_instantiator::call<test_iterator<input_iterator_tag, CanDifference::no>>();

iterator_instantiator::call<test_iterator<forward_iterator_tag, CanDifference::no>>();
iterator_instantiator::call<test_iterator<forward_iterator_tag, CanDifference::yes>>();

iterator_instantiator::call<test_iterator<bidirectional_iterator_tag, CanDifference::no>>();
iterator_instantiator::call<test_iterator<bidirectional_iterator_tag, CanDifference::yes>>();

iterator_instantiator::call<test_iterator<random_access_iterator_tag, CanDifference::yes>>();
iterator_instantiator::call<test_iterator<contiguous_iterator_tag, CanDifference::yes>>();
}

int main() {
static_assert((iterator_instantiation_test(), true));
iterator_instantiation_test();
}
7 changes: 5 additions & 2 deletions tests/std/tests/P0896R4_views_transform/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -242,14 +242,14 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) {
STATIC_ASSERT(CanEnd<const R&> == (range<const V> && const_invocable));
if (!is_empty) {
same_as<sentinel_t<R>> auto s = r.end();
static_assert(is_same_v<sentinel_t<R>, iterator_t<R>> == common_range<V>);
STATIC_ASSERT(is_same_v<sentinel_t<R>, iterator_t<R>> == common_range<V>);
if constexpr (bidirectional_range<R> && common_range<R>) {
assert(*prev(s) == *prev(end(expected)));
}

if constexpr (CanEnd<const R&>) {
same_as<sentinel_t<const R>> auto sc = as_const(r).end();
static_assert(is_same_v<sentinel_t<const R>, iterator_t<const R>> == common_range<const V>);
STATIC_ASSERT(is_same_v<sentinel_t<const R>, iterator_t<const R>> == common_range<const V>);
if constexpr (bidirectional_range<const R> && common_range<const R>) {
assert(*prev(sc) == *prev(end(expected)));
}
Expand Down Expand Up @@ -457,8 +457,11 @@ struct iterator_instantiator {
copyAssigned = copyConstructed;
assert(copyAssigned == valueConstructed);
STATIC_ASSERT(is_nothrow_copy_assignable_v<I>);
STATIC_ASSERT(same_as<const Iter&, decltype(as_const(copyConstructed).base())>);
}
assert(as_const(valueConstructed).base().peek() == mutable_ints);
assert(move(valueConstructed).base().peek() == mutable_ints);
STATIC_ASSERT(same_as<Iter, decltype(move(valueConstructed).base())>);

if constexpr (forward_iterator<Iter>) {
auto r1 = make_view();
Expand Down