llvm/libcxx/test/std/ranges/range.adaptors/range.filter/iterator/arrow.pass.cpp

//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17

// constexpr iterator_t<V> operator->() const
//    requires has-arrow<iterator_t<V>> && copyable<iterator_t<V>>

#include <ranges>

#include <array>
#include <cassert>
#include <concepts>
#include <cstddef>
#include <utility>
#include "test_iterators.h"
#include "test_macros.h"
#include "../types.h"

struct XYPoint {
  int x;
  int y;
};

template <class T>
concept has_arrow = requires (T t) {
  { t->x };
};
static_assert(has_arrow<XYPoint*>); // test the test

struct WithArrowOperator {
  using iterator_category = std::input_iterator_tag;
  using difference_type = std::ptrdiff_t;
  using value_type = XYPoint;

  constexpr explicit WithArrowOperator(XYPoint* p) : p_(p) { }
  constexpr XYPoint& operator*() const { return *p_; }
  constexpr XYPoint* operator->() const { return p_; } // has arrow
  constexpr WithArrowOperator& operator++() { ++p_; return *this; }
  constexpr WithArrowOperator operator++(int) { return WithArrowOperator(p_++); }

  friend constexpr XYPoint* base(WithArrowOperator const& i) { return i.p_; }
  XYPoint* p_;
};
static_assert(std::input_iterator<WithArrowOperator>);

struct WithNonCopyableIterator : std::ranges::view_base {
  struct iterator {
    using iterator_category = std::input_iterator_tag;
    using difference_type = std::ptrdiff_t;
    using value_type = XYPoint;

    iterator(iterator const&) = delete; // not copyable
    iterator(iterator&&);
    iterator& operator=(iterator&&);
    XYPoint& operator*() const;
    XYPoint* operator->() const;
    iterator& operator++();
    iterator operator++(int);

    // We need this to use XYPoint* as a sentinel type below. sentinel_wrapper
    // can't be used because this iterator is not copyable.
    friend bool operator==(iterator const&, XYPoint*);
  };

  iterator begin() const;
  XYPoint* end() const;
};
static_assert(std::ranges::input_range<WithNonCopyableIterator>);

template <class Iter, class Sent = sentinel_wrapper<Iter>>
constexpr void test() {
  std::array<XYPoint, 5> array{{{0, 0}, {1, 1}, {2, 2}, {3, 3}, {4, 4}}};
  using View = minimal_view<Iter, Sent>;
  using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
  using FilterIterator = std::ranges::iterator_t<FilterView>;

  auto make_filter_view = [](auto begin, auto end, auto pred) {
    View view{Iter(begin), Sent(Iter(end))};
    return FilterView(std::move(view), pred);
  };

  for (std::ptrdiff_t n = 0; n != 5; ++n) {
    FilterView view = make_filter_view(array.data(), array.data() + array.size(), AlwaysTrue{});
    FilterIterator const iter(view, Iter(array.data() + n));
    std::same_as<Iter> decltype(auto) result = iter.operator->();
    assert(base(result) == array.data() + n);
    assert(iter->x == n);
    assert(iter->y == n);
  }
}

constexpr bool tests() {
  test<WithArrowOperator>();
  test<XYPoint*>();
  test<XYPoint const*>();
  test<contiguous_iterator<XYPoint*>>();
  test<contiguous_iterator<XYPoint const*>>();

  // Make sure filter_view::iterator doesn't have operator-> if the
  // underlying iterator doesn't have one.
  {
    auto check_no_arrow = []<class It> {
      using View = minimal_view<It, sentinel_wrapper<It>>;
      using FilterView = std::ranges::filter_view<View, AlwaysTrue>;
      using FilterIterator = std::ranges::iterator_t<FilterView>;
      static_assert(!has_arrow<FilterIterator>);
    };
    check_no_arrow.operator()<cpp17_input_iterator<XYPoint*>>();
    check_no_arrow.operator()<cpp20_input_iterator<XYPoint*>>();
    check_no_arrow.operator()<forward_iterator<XYPoint*>>();
    check_no_arrow.operator()<bidirectional_iterator<XYPoint*>>();
    check_no_arrow.operator()<random_access_iterator<XYPoint*>>();
    check_no_arrow.operator()<int*>();
  }

  // Make sure filter_view::iterator doesn't have operator-> if the
  // underlying iterator is not copyable.
  {
    using FilterView = std::ranges::filter_view<WithNonCopyableIterator, AlwaysTrue>;
    using FilterIterator = std::ranges::iterator_t<FilterView>;
    static_assert(!has_arrow<FilterIterator>);
  }
  return true;
}

int main(int, char**) {
  tests();
  static_assert(tests());
  return 0;
}