|  | //===- IteratorTest.cpp - Unit tests for iterator utilities ---------------===// | 
|  | // | 
|  | // 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 | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "llvm/ADT/iterator.h" | 
|  | #include "llvm/ADT/ArrayRef.h" | 
|  | #include "llvm/ADT/STLExtras.h" | 
|  | #include "llvm/ADT/SmallVector.h" | 
|  | #include "llvm/ADT/ilist.h" | 
|  | #include "gmock/gmock.h" | 
|  | #include "gtest/gtest.h" | 
|  | #include <optional> | 
|  | #include <type_traits> | 
|  | #include <vector> | 
|  |  | 
|  | using namespace llvm; | 
|  | using testing::ElementsAre; | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | template <int> struct Shadow; | 
|  |  | 
|  | struct WeirdIter | 
|  | : llvm::iterator_facade_base<WeirdIter, std::input_iterator_tag, Shadow<0>, | 
|  | Shadow<1>, Shadow<2>, Shadow<3>> {}; | 
|  |  | 
|  | struct AdaptedIter : iterator_adaptor_base<AdaptedIter, WeirdIter> {}; | 
|  |  | 
|  | // Test that iterator_adaptor_base forwards typedefs, if value_type is | 
|  | // unchanged. | 
|  | static_assert(std::is_same_v<typename AdaptedIter::value_type, Shadow<0>>, ""); | 
|  | static_assert(std::is_same_v<typename AdaptedIter::difference_type, Shadow<1>>, | 
|  | ""); | 
|  | static_assert(std::is_same_v<typename AdaptedIter::pointer, Shadow<2>>, ""); | 
|  | static_assert(std::is_same_v<typename AdaptedIter::reference, Shadow<3>>, ""); | 
|  |  | 
|  | // Ensure that pointe{e,r}_iterator adaptors correctly forward the category of | 
|  | // the underlying iterator. | 
|  |  | 
|  | using RandomAccessIter = SmallVectorImpl<int*>::iterator; | 
|  | using BidiIter = ilist<int*>::iterator; | 
|  |  | 
|  | template<class T> | 
|  | using pointee_iterator_defaulted = pointee_iterator<T>; | 
|  | template<class T> | 
|  | using pointer_iterator_defaulted = pointer_iterator<T>; | 
|  |  | 
|  | // Ensures that an iterator and its adaptation have the same iterator_category. | 
|  | template<template<typename> class A, typename It> | 
|  | using IsAdaptedIterCategorySame = | 
|  | std::is_same<typename std::iterator_traits<It>::iterator_category, | 
|  | typename std::iterator_traits<A<It>>::iterator_category>; | 
|  |  | 
|  | // Check that dereferencing works correctly adapting pointers and proxies. | 
|  | template <class T> | 
|  | struct PointerWrapper : public iterator_adaptor_base<PointerWrapper<T>, T *> { | 
|  | PointerWrapper(T *I) : PointerWrapper::iterator_adaptor_base(I) {} | 
|  | }; | 
|  | struct IntProxy { | 
|  | int &I; | 
|  | IntProxy(int &I) : I(I) {} | 
|  | void operator=(int NewValue) { I = NewValue; } | 
|  | }; | 
|  | struct ConstIntProxy { | 
|  | const int &I; | 
|  | ConstIntProxy(const int &I) : I(I) {} | 
|  | }; | 
|  | template <class T, class ProxyT> | 
|  | struct PointerProxyWrapper | 
|  | : public iterator_adaptor_base<PointerProxyWrapper<T, ProxyT>, T *, | 
|  | std::random_access_iterator_tag, T, | 
|  | ptrdiff_t, T *, ProxyT> { | 
|  | PointerProxyWrapper(T *I) : PointerProxyWrapper::iterator_adaptor_base(I) {} | 
|  | }; | 
|  | using IntIterator = PointerWrapper<int>; | 
|  | using ConstIntIterator = PointerWrapper<const int>; | 
|  | using IntProxyIterator = PointerProxyWrapper<int, IntProxy>; | 
|  | using ConstIntProxyIterator = PointerProxyWrapper<const int, ConstIntProxy>; | 
|  |  | 
|  | // There should only be a single (const-qualified) operator*, operator->, and | 
|  | // operator[]. This test confirms that there isn't a non-const overload. Rather | 
|  | // than adding those, users should double-check that T, PointerT, and ReferenceT | 
|  | // have the right constness, and/or make fields mutable. | 
|  | static_assert(&IntIterator::operator* == &IntIterator::operator*, ""); | 
|  | static_assert(&IntIterator::operator-> == &IntIterator::operator->, ""); | 
|  | static_assert(&IntIterator::operator[] == &IntIterator::operator[], ""); | 
|  |  | 
|  | template <class T, std::enable_if_t<std::is_assignable_v<T, int>, bool> = false> | 
|  | constexpr bool canAssignFromInt(T &&) { | 
|  | return true; | 
|  | } | 
|  | template <class T, | 
|  | std::enable_if_t<!std::is_assignable_v<T, int>, bool> = false> | 
|  | constexpr bool canAssignFromInt(T &&) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | TEST(IteratorAdaptorTest, Dereference) { | 
|  | int Number = 1; | 
|  |  | 
|  | // Construct some iterators and check whether they can be assigned to. | 
|  | IntIterator I(&Number); | 
|  | const IntIterator IC(&Number); | 
|  | ConstIntIterator CI(&Number); | 
|  | const ConstIntIterator CIC(&Number); | 
|  | EXPECT_EQ(true, canAssignFromInt(*I));    // int * | 
|  | EXPECT_EQ(true, canAssignFromInt(*IC));   // int *const | 
|  | EXPECT_EQ(false, canAssignFromInt(*CI));  // const int * | 
|  | EXPECT_EQ(false, canAssignFromInt(*CIC)); // const int *const | 
|  |  | 
|  | // Prove that dereference and assignment work. | 
|  | EXPECT_EQ(1, *I); | 
|  | EXPECT_EQ(1, *IC); | 
|  | EXPECT_EQ(1, *CI); | 
|  | EXPECT_EQ(1, *CIC); | 
|  | *I = 2; | 
|  | EXPECT_EQ(2, Number); | 
|  | *IC = 3; | 
|  | EXPECT_EQ(3, Number); | 
|  |  | 
|  | // Construct some proxy iterators and check whether they can be assigned to. | 
|  | IntProxyIterator P(&Number); | 
|  | const IntProxyIterator PC(&Number); | 
|  | ConstIntProxyIterator CP(&Number); | 
|  | const ConstIntProxyIterator CPC(&Number); | 
|  | EXPECT_EQ(true, canAssignFromInt(*P));    // int * | 
|  | EXPECT_EQ(true, canAssignFromInt(*PC));   // int *const | 
|  | EXPECT_EQ(false, canAssignFromInt(*CP));  // const int * | 
|  | EXPECT_EQ(false, canAssignFromInt(*CPC)); // const int *const | 
|  |  | 
|  | // Prove that dereference and assignment work. | 
|  | EXPECT_EQ(3, (*P).I); | 
|  | EXPECT_EQ(3, (*PC).I); | 
|  | EXPECT_EQ(3, (*CP).I); | 
|  | EXPECT_EQ(3, (*CPC).I); | 
|  | *P = 4; | 
|  | EXPECT_EQ(4, Number); | 
|  | *PC = 5; | 
|  | EXPECT_EQ(5, Number); | 
|  | } | 
|  |  | 
|  | // pointeE_iterator | 
|  | static_assert(IsAdaptedIterCategorySame<pointee_iterator_defaulted, | 
|  | RandomAccessIter>::value, ""); | 
|  | static_assert(IsAdaptedIterCategorySame<pointee_iterator_defaulted, | 
|  | BidiIter>::value, ""); | 
|  | // pointeR_iterator | 
|  | static_assert(IsAdaptedIterCategorySame<pointer_iterator_defaulted, | 
|  | RandomAccessIter>::value, ""); | 
|  | static_assert(IsAdaptedIterCategorySame<pointer_iterator_defaulted, | 
|  | BidiIter>::value, ""); | 
|  |  | 
|  | TEST(PointeeIteratorTest, Basic) { | 
|  | int arr[4] = {1, 2, 3, 4}; | 
|  | SmallVector<int *, 4> V; | 
|  | V.push_back(&arr[0]); | 
|  | V.push_back(&arr[1]); | 
|  | V.push_back(&arr[2]); | 
|  | V.push_back(&arr[3]); | 
|  |  | 
|  | typedef pointee_iterator<SmallVectorImpl<int *>::const_iterator> | 
|  | test_iterator; | 
|  |  | 
|  | test_iterator Begin, End; | 
|  | Begin = V.begin(); | 
|  | End = test_iterator(V.end()); | 
|  |  | 
|  | test_iterator I = Begin; | 
|  | for (int i = 0; i < 4; ++i) { | 
|  | EXPECT_EQ(*V[i], *I); | 
|  |  | 
|  | EXPECT_EQ(I, Begin + i); | 
|  | EXPECT_EQ(I, std::next(Begin, i)); | 
|  | test_iterator J = Begin; | 
|  | J += i; | 
|  | EXPECT_EQ(I, J); | 
|  | EXPECT_EQ(*V[i], Begin[i]); | 
|  |  | 
|  | EXPECT_NE(I, End); | 
|  | EXPECT_GT(End, I); | 
|  | EXPECT_LT(I, End); | 
|  | EXPECT_GE(I, Begin); | 
|  | EXPECT_LE(Begin, I); | 
|  |  | 
|  | EXPECT_EQ(i, I - Begin); | 
|  | EXPECT_EQ(i, std::distance(Begin, I)); | 
|  | EXPECT_EQ(Begin, I - i); | 
|  |  | 
|  | test_iterator K = I++; | 
|  | EXPECT_EQ(K, std::prev(I)); | 
|  | } | 
|  | EXPECT_EQ(End, I); | 
|  | } | 
|  |  | 
|  | TEST(PointeeIteratorTest, SmartPointer) { | 
|  | SmallVector<std::unique_ptr<int>, 4> V; | 
|  | V.push_back(std::make_unique<int>(1)); | 
|  | V.push_back(std::make_unique<int>(2)); | 
|  | V.push_back(std::make_unique<int>(3)); | 
|  | V.push_back(std::make_unique<int>(4)); | 
|  |  | 
|  | typedef pointee_iterator< | 
|  | SmallVectorImpl<std::unique_ptr<int>>::const_iterator> | 
|  | test_iterator; | 
|  |  | 
|  | test_iterator Begin, End; | 
|  | Begin = V.begin(); | 
|  | End = test_iterator(V.end()); | 
|  |  | 
|  | test_iterator I = Begin; | 
|  | for (int i = 0; i < 4; ++i) { | 
|  | EXPECT_EQ(*V[i], *I); | 
|  |  | 
|  | EXPECT_EQ(I, Begin + i); | 
|  | EXPECT_EQ(I, std::next(Begin, i)); | 
|  | test_iterator J = Begin; | 
|  | J += i; | 
|  | EXPECT_EQ(I, J); | 
|  | EXPECT_EQ(*V[i], Begin[i]); | 
|  |  | 
|  | EXPECT_NE(I, End); | 
|  | EXPECT_GT(End, I); | 
|  | EXPECT_LT(I, End); | 
|  | EXPECT_GE(I, Begin); | 
|  | EXPECT_LE(Begin, I); | 
|  |  | 
|  | EXPECT_EQ(i, I - Begin); | 
|  | EXPECT_EQ(i, std::distance(Begin, I)); | 
|  | EXPECT_EQ(Begin, I - i); | 
|  |  | 
|  | test_iterator K = I++; | 
|  | EXPECT_EQ(K, std::prev(I)); | 
|  | } | 
|  | EXPECT_EQ(End, I); | 
|  | } | 
|  |  | 
|  | TEST(PointeeIteratorTest, Range) { | 
|  | int A[] = {1, 2, 3, 4}; | 
|  | SmallVector<int *, 4> V{&A[0], &A[1], &A[2], &A[3]}; | 
|  |  | 
|  | int I = 0; | 
|  | for (int II : make_pointee_range(V)) | 
|  | EXPECT_EQ(A[I++], II); | 
|  | } | 
|  |  | 
|  | TEST(PointeeIteratorTest, PointeeType) { | 
|  | struct S { | 
|  | int X; | 
|  | bool operator==(const S &RHS) const { return X == RHS.X; }; | 
|  | }; | 
|  | S A[] = {S{0}, S{1}}; | 
|  | SmallVector<S *, 2> V{&A[0], &A[1]}; | 
|  |  | 
|  | pointee_iterator<SmallVectorImpl<S *>::const_iterator, const S> I = V.begin(); | 
|  | for (int j = 0; j < 2; ++j, ++I) { | 
|  | EXPECT_EQ(*V[j], *I); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST(FilterIteratorTest, Lambda) { | 
|  | auto IsOdd = [](int N) { return N % 2 == 1; }; | 
|  | int A[] = {0, 1, 2, 3, 4, 5, 6}; | 
|  | auto Range = make_filter_range(A, IsOdd); | 
|  | SmallVector<int, 3> Actual(Range.begin(), Range.end()); | 
|  | EXPECT_EQ((SmallVector<int, 3>{1, 3, 5}), Actual); | 
|  | } | 
|  |  | 
|  | TEST(FilterIteratorTest, Enumerate) { | 
|  | auto IsOdd = [](auto N) { return N.value() % 2 == 1; }; | 
|  | int A[] = {0, 1, 2, 3, 4, 5, 6}; | 
|  | auto Enumerate = llvm::enumerate(A); | 
|  | SmallVector<int> Actual; | 
|  | for (const auto &IndexedValue : make_filter_range(Enumerate, IsOdd)) | 
|  | Actual.push_back(IndexedValue.value()); | 
|  | EXPECT_EQ((SmallVector<int, 3>{1, 3, 5}), Actual); | 
|  | } | 
|  |  | 
|  | TEST(FilterIteratorTest, CallableObject) { | 
|  | int Counter = 0; | 
|  | struct Callable { | 
|  | int &Counter; | 
|  |  | 
|  | Callable(int &Counter) : Counter(Counter) {} | 
|  |  | 
|  | bool operator()(int N) { | 
|  | Counter++; | 
|  | return N % 2 == 1; | 
|  | } | 
|  | }; | 
|  | Callable IsOdd(Counter); | 
|  | int A[] = {0, 1, 2, 3, 4, 5, 6}; | 
|  | auto Range = make_filter_range(A, IsOdd); | 
|  | EXPECT_EQ(2, Counter); | 
|  | SmallVector<int, 3> Actual(Range.begin(), Range.end()); | 
|  | EXPECT_GE(Counter, 7); | 
|  | EXPECT_EQ((SmallVector<int, 3>{1, 3, 5}), Actual); | 
|  | } | 
|  |  | 
|  | TEST(FilterIteratorTest, FunctionPointer) { | 
|  | bool (*IsOdd)(int) = [](int N) { return N % 2 == 1; }; | 
|  | int A[] = {0, 1, 2, 3, 4, 5, 6}; | 
|  | auto Range = make_filter_range(A, IsOdd); | 
|  | SmallVector<int, 3> Actual(Range.begin(), Range.end()); | 
|  | EXPECT_EQ((SmallVector<int, 3>{1, 3, 5}), Actual); | 
|  | } | 
|  |  | 
|  | TEST(FilterIteratorTest, Composition) { | 
|  | auto IsOdd = [](int N) { return N % 2 == 1; }; | 
|  | std::unique_ptr<int> A[] = {std::make_unique<int>(0), std::make_unique<int>(1), | 
|  | std::make_unique<int>(2), std::make_unique<int>(3), | 
|  | std::make_unique<int>(4), std::make_unique<int>(5), | 
|  | std::make_unique<int>(6)}; | 
|  | using PointeeIterator = pointee_iterator<std::unique_ptr<int> *>; | 
|  | auto Range = make_filter_range( | 
|  | make_range(PointeeIterator(std::begin(A)), PointeeIterator(std::end(A))), | 
|  | IsOdd); | 
|  | SmallVector<int, 3> Actual(Range.begin(), Range.end()); | 
|  | EXPECT_EQ((SmallVector<int, 3>{1, 3, 5}), Actual); | 
|  | } | 
|  |  | 
|  | TEST(FilterIteratorTest, InputIterator) { | 
|  | struct InputIterator | 
|  | : iterator_adaptor_base<InputIterator, int *, std::input_iterator_tag> { | 
|  | InputIterator(int *It) : InputIterator::iterator_adaptor_base(It) {} | 
|  | }; | 
|  |  | 
|  | auto IsOdd = [](int N) { return N % 2 == 1; }; | 
|  | int A[] = {0, 1, 2, 3, 4, 5, 6}; | 
|  | auto Range = make_filter_range( | 
|  | make_range(InputIterator(std::begin(A)), InputIterator(std::end(A))), | 
|  | IsOdd); | 
|  | SmallVector<int, 3> Actual(Range.begin(), Range.end()); | 
|  | EXPECT_EQ((SmallVector<int, 3>{1, 3, 5}), Actual); | 
|  | } | 
|  |  | 
|  | TEST(FilterIteratorTest, ReverseFilterRange) { | 
|  | auto IsOdd = [](int N) { return N % 2 == 1; }; | 
|  | int A[] = {0, 1, 2, 3, 4, 5, 6}; | 
|  |  | 
|  | // Check basic reversal. | 
|  | auto Range = reverse(make_filter_range(A, IsOdd)); | 
|  | SmallVector<int, 3> Actual(Range.begin(), Range.end()); | 
|  | EXPECT_EQ((SmallVector<int, 3>{5, 3, 1}), Actual); | 
|  |  | 
|  | // Check that the reverse of the reverse is the original. | 
|  | auto Range2 = reverse(reverse(make_filter_range(A, IsOdd))); | 
|  | SmallVector<int, 3> Actual2(Range2.begin(), Range2.end()); | 
|  | EXPECT_EQ((SmallVector<int, 3>{1, 3, 5}), Actual2); | 
|  |  | 
|  | // Check empty ranges. | 
|  | auto Range3 = reverse(make_filter_range(ArrayRef<int>(), IsOdd)); | 
|  | SmallVector<int, 0> Actual3(Range3.begin(), Range3.end()); | 
|  | EXPECT_EQ((SmallVector<int, 0>{}), Actual3); | 
|  |  | 
|  | // Check that we don't skip the first element, provided it isn't filtered | 
|  | // away. | 
|  | auto IsEven = [](int N) { return N % 2 == 0; }; | 
|  | auto Range4 = reverse(make_filter_range(A, IsEven)); | 
|  | SmallVector<int, 4> Actual4(Range4.begin(), Range4.end()); | 
|  | EXPECT_EQ((SmallVector<int, 4>{6, 4, 2, 0}), Actual4); | 
|  | } | 
|  |  | 
|  | TEST(PointerIterator, Basic) { | 
|  | int A[] = {1, 2, 3, 4}; | 
|  | pointer_iterator<int *> Begin(std::begin(A)), End(std::end(A)); | 
|  | EXPECT_EQ(A, *Begin); | 
|  | ++Begin; | 
|  | EXPECT_EQ(A + 1, *Begin); | 
|  | ++Begin; | 
|  | EXPECT_EQ(A + 2, *Begin); | 
|  | ++Begin; | 
|  | EXPECT_EQ(A + 3, *Begin); | 
|  | ++Begin; | 
|  | EXPECT_EQ(Begin, End); | 
|  | } | 
|  |  | 
|  | TEST(PointerIterator, Const) { | 
|  | int A[] = {1, 2, 3, 4}; | 
|  | const pointer_iterator<int *> Begin(std::begin(A)); | 
|  | EXPECT_EQ(A, *Begin); | 
|  | EXPECT_EQ(A + 1, std::next(*Begin, 1)); | 
|  | EXPECT_EQ(A + 2, std::next(*Begin, 2)); | 
|  | EXPECT_EQ(A + 3, std::next(*Begin, 3)); | 
|  | EXPECT_EQ(A + 4, std::next(*Begin, 4)); | 
|  | } | 
|  |  | 
|  | TEST(PointerIterator, Range) { | 
|  | int A[] = {1, 2, 3, 4}; | 
|  | int I = 0; | 
|  | for (int *P : make_pointer_range(A)) | 
|  | EXPECT_EQ(A + I++, P); | 
|  | } | 
|  |  | 
|  | namespace rbegin_detail { | 
|  | struct WithFreeRBegin { | 
|  | int data[3] = {42, 43, 44}; | 
|  | }; | 
|  |  | 
|  | auto rbegin(const WithFreeRBegin &X) { return std::rbegin(X.data); } | 
|  | auto rend(const WithFreeRBegin &X) { return std::rend(X.data); } | 
|  | } // namespace rbegin_detail | 
|  |  | 
|  | TEST(ReverseTest, ADL) { | 
|  | // Check that we can find the rbegin/rend functions via ADL. | 
|  | rbegin_detail::WithFreeRBegin Foo; | 
|  | EXPECT_THAT(reverse(Foo), ElementsAre(44, 43, 42)); | 
|  | } | 
|  |  | 
|  | TEST(ZipIteratorTest, Basic) { | 
|  | using namespace std; | 
|  | const SmallVector<unsigned, 6> pi{3, 1, 4, 1, 5, 9}; | 
|  | SmallVector<bool, 6> odd{1, 1, 0, 1, 1, 1}; | 
|  | const char message[] = "yynyyy\0"; | 
|  | std::array<int, 2> shortArr = {42, 43}; | 
|  |  | 
|  | for (auto tup : zip(pi, odd, message)) { | 
|  | EXPECT_EQ(get<0>(tup) & 0x01, get<1>(tup)); | 
|  | EXPECT_EQ(get<0>(tup) & 0x01 ? 'y' : 'n', get<2>(tup)); | 
|  | } | 
|  |  | 
|  | // Note the rvalue. | 
|  | for (auto tup : zip(pi, SmallVector<bool, 0>{1, 1, 0, 1, 1})) { | 
|  | EXPECT_EQ(get<0>(tup) & 0x01, get<1>(tup)); | 
|  | } | 
|  |  | 
|  | // Iterate until we run out elements in the *shortest* range. | 
|  | for (auto [idx, elem] : enumerate(zip(odd, shortArr))) { | 
|  | EXPECT_LT(idx, static_cast<size_t>(2)); | 
|  | } | 
|  | for (auto [idx, elem] : enumerate(zip(shortArr, odd))) { | 
|  | EXPECT_LT(idx, static_cast<size_t>(2)); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST(ZipIteratorTest, ZipEqualBasic) { | 
|  | const SmallVector<unsigned, 6> pi = {3, 1, 4, 1, 5, 8}; | 
|  | const SmallVector<bool, 6> vals = {1, 1, 0, 1, 1, 0}; | 
|  | unsigned iters = 0; | 
|  |  | 
|  | for (auto [lhs, rhs] : zip_equal(vals, pi)) { | 
|  | EXPECT_EQ(lhs, rhs & 0x01); | 
|  | ++iters; | 
|  | } | 
|  |  | 
|  | EXPECT_EQ(iters, 6u); | 
|  | } | 
|  |  | 
|  | template <typename T> | 
|  | constexpr bool IsConstRef = | 
|  | std::is_reference_v<T> && std::is_const_v<std::remove_reference_t<T>>; | 
|  |  | 
|  | template <typename T> | 
|  | constexpr bool IsBoolConstRef = | 
|  | std::is_same_v<llvm::remove_cvref_t<T>, std::vector<bool>::const_reference>; | 
|  |  | 
|  | /// Returns a `const` copy of the passed value. The `const` on the returned | 
|  | /// value is intentional here so that `MakeConst` can be used in range-for | 
|  | /// loops. | 
|  | template <typename T> const T MakeConst(T &&value) { | 
|  | return std::forward<T>(value); | 
|  | } | 
|  |  | 
|  | TEST(ZipIteratorTest, ZipEqualConstCorrectness) { | 
|  | const std::vector<unsigned> c_first = {3, 1, 4}; | 
|  | std::vector<unsigned> first = c_first; | 
|  | const SmallVector<bool> c_second = {1, 1, 0}; | 
|  | SmallVector<bool> second = c_second; | 
|  |  | 
|  | for (auto [a, b, c, d] : zip_equal(c_first, first, c_second, second)) { | 
|  | b = 0; | 
|  | d = true; | 
|  | static_assert(IsConstRef<decltype(a)>); | 
|  | static_assert(!IsConstRef<decltype(b)>); | 
|  | static_assert(IsConstRef<decltype(c)>); | 
|  | static_assert(!IsConstRef<decltype(d)>); | 
|  | } | 
|  |  | 
|  | EXPECT_THAT(first, ElementsAre(0, 0, 0)); | 
|  | EXPECT_THAT(second, ElementsAre(true, true, true)); | 
|  |  | 
|  | std::vector<bool> nemesis = {true, false, true}; | 
|  | const std::vector<bool> c_nemesis = nemesis; | 
|  |  | 
|  | for (auto &&[a, b, c, d] : zip_equal(first, c_first, nemesis, c_nemesis)) { | 
|  | a = 2; | 
|  | c = true; | 
|  | static_assert(!IsConstRef<decltype(a)>); | 
|  | static_assert(IsConstRef<decltype(b)>); | 
|  | static_assert(!IsBoolConstRef<decltype(c)>); | 
|  | static_assert(IsBoolConstRef<decltype(d)>); | 
|  | } | 
|  |  | 
|  | EXPECT_THAT(first, ElementsAre(2, 2, 2)); | 
|  | EXPECT_THAT(nemesis, ElementsAre(true, true, true)); | 
|  |  | 
|  | unsigned iters = 0; | 
|  | for (const auto &[a, b, c, d] : | 
|  | zip_equal(first, c_first, nemesis, c_nemesis)) { | 
|  | static_assert(!IsConstRef<decltype(a)>); | 
|  | static_assert(IsConstRef<decltype(b)>); | 
|  | static_assert(!IsBoolConstRef<decltype(c)>); | 
|  | static_assert(IsBoolConstRef<decltype(d)>); | 
|  | ++iters; | 
|  | } | 
|  | EXPECT_EQ(iters, 3u); | 
|  | iters = 0; | 
|  |  | 
|  | for (const auto &[a, b, c, d] : | 
|  | MakeConst(zip_equal(first, c_first, nemesis, c_nemesis))) { | 
|  | static_assert(!IsConstRef<decltype(a)>); | 
|  | static_assert(IsConstRef<decltype(b)>); | 
|  | static_assert(!IsBoolConstRef<decltype(c)>); | 
|  | static_assert(IsBoolConstRef<decltype(d)>); | 
|  | ++iters; | 
|  | } | 
|  | EXPECT_EQ(iters, 3u); | 
|  | } | 
|  |  | 
|  | TEST(ZipIteratorTest, ZipEqualTemporaries) { | 
|  | unsigned iters = 0; | 
|  |  | 
|  | // These temporary ranges get moved into the `tuple<...> storage;` inside | 
|  | // `zippy`. From then on, we can use references obtained from this storage to | 
|  | // access them. This does not rely on any lifetime extensions on the | 
|  | // temporaries passed to `zip_equal`. | 
|  | for (auto [a, b, c] : zip_equal(SmallVector<int>{1, 2, 3}, std::string("abc"), | 
|  | std::vector<bool>{true, false, true})) { | 
|  | a = 3; | 
|  | b = 'c'; | 
|  | c = false; | 
|  | static_assert(!IsConstRef<decltype(a)>); | 
|  | static_assert(!IsConstRef<decltype(b)>); | 
|  | static_assert(!IsBoolConstRef<decltype(c)>); | 
|  | ++iters; | 
|  | } | 
|  | EXPECT_EQ(iters, 3u); | 
|  | iters = 0; | 
|  |  | 
|  | for (auto [a, b, c] : | 
|  | MakeConst(zip_equal(SmallVector<int>{1, 2, 3}, std::string("abc"), | 
|  | std::vector<bool>{true, false, true}))) { | 
|  | static_assert(IsConstRef<decltype(a)>); | 
|  | static_assert(IsConstRef<decltype(b)>); | 
|  | static_assert(IsBoolConstRef<decltype(c)>); | 
|  | ++iters; | 
|  | } | 
|  | EXPECT_EQ(iters, 3u); | 
|  | } | 
|  |  | 
|  | #if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST | 
|  | // Check that an assertion is triggered when ranges passed to `zip_equal` differ | 
|  | // in length. | 
|  | TEST(ZipIteratorTest, ZipEqualNotEqual) { | 
|  | const SmallVector<unsigned, 6> pi = {3, 1, 4, 1, 5, 8}; | 
|  | const SmallVector<bool, 2> vals = {1, 1}; | 
|  |  | 
|  | EXPECT_DEATH(zip_equal(pi, vals), "Iteratees do not have equal length"); | 
|  | EXPECT_DEATH(zip_equal(vals, pi), "Iteratees do not have equal length"); | 
|  | EXPECT_DEATH(zip_equal(pi, pi, vals), "Iteratees do not have equal length"); | 
|  | EXPECT_DEATH(zip_equal(vals, vals, pi), "Iteratees do not have equal length"); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | TEST(ZipIteratorTest, ZipFirstBasic) { | 
|  | using namespace std; | 
|  | const SmallVector<unsigned, 6> pi{3, 1, 4, 1, 5, 9}; | 
|  | unsigned iters = 0; | 
|  |  | 
|  | for (auto tup : zip_first(SmallVector<bool, 0>{1, 1, 0, 1}, pi)) { | 
|  | EXPECT_EQ(get<0>(tup), get<1>(tup) & 0x01); | 
|  | iters += 1; | 
|  | } | 
|  |  | 
|  | EXPECT_EQ(iters, 4u); | 
|  | } | 
|  |  | 
|  | #if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST | 
|  | // Make sure that we can detect when the first range is not the shortest. | 
|  | TEST(ZipIteratorTest, ZipFirstNotShortest) { | 
|  | const std::array<unsigned, 6> longer = {}; | 
|  | const std::array<unsigned, 4> shorter = {}; | 
|  |  | 
|  | EXPECT_DEATH(zip_first(longer, shorter), | 
|  | "First iteratee is not the shortest"); | 
|  | EXPECT_DEATH(zip_first(longer, shorter, longer), | 
|  | "First iteratee is not the shortest"); | 
|  | EXPECT_DEATH(zip_first(longer, longer, shorter), | 
|  | "First iteratee is not the shortest"); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | TEST(ZipIteratorTest, ZipLongestBasic) { | 
|  | using namespace std; | 
|  | const vector<unsigned> pi{3, 1, 4, 1, 5, 9}; | 
|  | const vector<StringRef> e{"2", "7", "1", "8"}; | 
|  |  | 
|  | { | 
|  | // Check left range longer than right. | 
|  | const vector<tuple<optional<unsigned>, optional<StringRef>>> expected{ | 
|  | make_tuple(3, StringRef("2")), make_tuple(1, StringRef("7")), | 
|  | make_tuple(4, StringRef("1")), make_tuple(1, StringRef("8")), | 
|  | make_tuple(5, std::nullopt),   make_tuple(9, std::nullopt)}; | 
|  | size_t iters = 0; | 
|  | for (auto tup : zip_longest(pi, e)) { | 
|  | EXPECT_EQ(tup, expected[iters]); | 
|  | iters += 1; | 
|  | } | 
|  | EXPECT_EQ(iters, expected.size()); | 
|  | } | 
|  |  | 
|  | { | 
|  | // Check right range longer than left. | 
|  | const vector<tuple<optional<StringRef>, optional<unsigned>>> expected{ | 
|  | make_tuple(StringRef("2"), 3), make_tuple(StringRef("7"), 1), | 
|  | make_tuple(StringRef("1"), 4), make_tuple(StringRef("8"), 1), | 
|  | make_tuple(std::nullopt, 5),   make_tuple(std::nullopt, 9)}; | 
|  | size_t iters = 0; | 
|  | for (auto tup : zip_longest(e, pi)) { | 
|  | EXPECT_EQ(tup, expected[iters]); | 
|  | iters += 1; | 
|  | } | 
|  | EXPECT_EQ(iters, expected.size()); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST(ZipIteratorTest, Mutability) { | 
|  | using namespace std; | 
|  | const SmallVector<unsigned, 4> pi{3, 1, 4, 1, 5, 9}; | 
|  | char message[] = "hello zip\0"; | 
|  |  | 
|  | for (auto tup : zip(pi, message, message)) { | 
|  | EXPECT_EQ(get<1>(tup), get<2>(tup)); | 
|  | get<2>(tup) = get<0>(tup) & 0x01 ? 'y' : 'n'; | 
|  | } | 
|  |  | 
|  | // note the rvalue | 
|  | for (auto tup : zip(message, "yynyyyzip\0")) { | 
|  | EXPECT_EQ(get<0>(tup), get<1>(tup)); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST(ZipIteratorTest, ZipFirstMutability) { | 
|  | using namespace std; | 
|  | vector<unsigned> pi{3, 1, 4, 1, 5, 9}; | 
|  | unsigned iters = 0; | 
|  |  | 
|  | for (auto tup : zip_first(SmallVector<bool, 0>{1, 1, 0, 1}, pi)) { | 
|  | get<1>(tup) = get<0>(tup); | 
|  | iters += 1; | 
|  | } | 
|  |  | 
|  | EXPECT_EQ(iters, 4u); | 
|  |  | 
|  | for (auto tup : zip_first(SmallVector<bool, 0>{1, 1, 0, 1}, pi)) { | 
|  | EXPECT_EQ(get<0>(tup), get<1>(tup)); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST(ZipIteratorTest, Filter) { | 
|  | using namespace std; | 
|  | vector<unsigned> pi{3, 1, 4, 1, 5, 9}; | 
|  |  | 
|  | unsigned iters = 0; | 
|  | // pi is length 6, but the zip RHS is length 7. | 
|  | auto zipped = zip_first(pi, vector<bool>{1, 1, 0, 1, 1, 1, 0}); | 
|  | for (auto tup : make_filter_range( | 
|  | zipped, [](decltype(zipped)::value_type t) { return get<1>(t); })) { | 
|  | EXPECT_EQ(get<0>(tup) & 0x01, get<1>(tup)); | 
|  | get<0>(tup) += 1; | 
|  | iters += 1; | 
|  | } | 
|  |  | 
|  | // Should have skipped pi[2]. | 
|  | EXPECT_EQ(iters, 5u); | 
|  |  | 
|  | // Ensure that in-place mutation works. | 
|  | EXPECT_TRUE(all_of(pi, [](unsigned n) { return (n & 0x01) == 0; })); | 
|  | } | 
|  |  | 
|  | TEST(ZipIteratorTest, Reverse) { | 
|  | using namespace std; | 
|  | vector<unsigned> ascending{0, 1, 2, 3, 4, 5}; | 
|  |  | 
|  | auto zipped = zip_first(ascending, vector<bool>{0, 1, 0, 1, 0, 1}); | 
|  | unsigned last = 6; | 
|  | for (auto tup : reverse(zipped)) { | 
|  | // Check that this is in reverse. | 
|  | EXPECT_LT(get<0>(tup), last); | 
|  | last = get<0>(tup); | 
|  | EXPECT_EQ(get<0>(tup) & 0x01, get<1>(tup)); | 
|  | } | 
|  |  | 
|  | auto odds = [](decltype(zipped)::value_type tup) { return get<1>(tup); }; | 
|  | last = 6; | 
|  | for (auto tup : make_filter_range(reverse(zipped), odds)) { | 
|  | EXPECT_LT(get<0>(tup), last); | 
|  | last = get<0>(tup); | 
|  | EXPECT_TRUE(get<0>(tup) & 0x01); | 
|  | get<0>(tup) += 1; | 
|  | } | 
|  |  | 
|  | // Ensure that in-place mutation works. | 
|  | EXPECT_TRUE(all_of(ascending, [](unsigned n) { return (n & 0x01) == 0; })); | 
|  | } | 
|  |  | 
|  | // Int iterator that keeps track of the number of its copies. | 
|  | struct CountingIntIterator : IntIterator { | 
|  | unsigned *cnt; | 
|  |  | 
|  | CountingIntIterator(int *it, unsigned &counter) | 
|  | : IntIterator(it), cnt(&counter) {} | 
|  |  | 
|  | CountingIntIterator(const CountingIntIterator &other) | 
|  | : IntIterator(other.I), cnt(other.cnt) { | 
|  | ++(*cnt); | 
|  | } | 
|  | CountingIntIterator &operator=(const CountingIntIterator &other) { | 
|  | this->I = other.I; | 
|  | this->cnt = other.cnt; | 
|  | ++(*cnt); | 
|  | return *this; | 
|  | } | 
|  | }; | 
|  |  | 
|  | // Check that the iterators do not get copied with each `zippy` iterator | 
|  | // increment. | 
|  | TEST(ZipIteratorTest, IteratorCopies) { | 
|  | std::vector<int> ints(1000, 42); | 
|  | unsigned total_copy_count = 0; | 
|  | CountingIntIterator begin(ints.data(), total_copy_count); | 
|  | CountingIntIterator end(ints.data() + ints.size(), total_copy_count); | 
|  |  | 
|  | size_t iters = 0; | 
|  | auto zippy = zip_equal(ints, llvm::make_range(begin, end)); | 
|  | const unsigned creation_copy_count = total_copy_count; | 
|  |  | 
|  | for (auto [a, b] : zippy) { | 
|  | EXPECT_EQ(a, b); | 
|  | ++iters; | 
|  | } | 
|  | EXPECT_EQ(iters, ints.size()); | 
|  |  | 
|  | // We expect the number of copies to be much smaller than the number of loop | 
|  | // iterations. | 
|  | unsigned loop_copy_count = total_copy_count - creation_copy_count; | 
|  | EXPECT_LT(loop_copy_count, 10u); | 
|  | } | 
|  |  | 
|  | TEST(RangeTest, Distance) { | 
|  | std::vector<int> v1; | 
|  | std::vector<int> v2{1, 2, 3}; | 
|  |  | 
|  | EXPECT_EQ(std::distance(v1.begin(), v1.end()), size(v1)); | 
|  | EXPECT_EQ(std::distance(v2.begin(), v2.end()), size(v2)); | 
|  | } | 
|  |  | 
|  | TEST(RangeSizeTest, CommonRangeTypes) { | 
|  | SmallVector<int> v1 = {1, 2, 3}; | 
|  | EXPECT_EQ(range_size(v1), 3u); | 
|  |  | 
|  | std::map<int, int> m1 = {{1, 1}, {2, 2}}; | 
|  | EXPECT_EQ(range_size(m1), 2u); | 
|  |  | 
|  | auto it_range = llvm::make_range(m1.begin(), m1.end()); | 
|  | EXPECT_EQ(range_size(it_range), 2u); | 
|  |  | 
|  | static constexpr int c_arr[5] = {}; | 
|  | static_assert(range_size(c_arr) == 5u); | 
|  |  | 
|  | static constexpr std::array<int, 6> cpp_arr = {}; | 
|  | static_assert(range_size(cpp_arr) == 6u); | 
|  | } | 
|  |  | 
|  | struct FooWithMemberSize { | 
|  | size_t size() const { return 42; } | 
|  | auto begin() { return Data.begin(); } | 
|  | auto end() { return Data.end(); } | 
|  |  | 
|  | std::set<int> Data; | 
|  | }; | 
|  |  | 
|  | TEST(RangeSizeTest, MemberSize) { | 
|  | // Make sure that member `.size()` is preferred over the free fuction and | 
|  | // `std::distance`. | 
|  | FooWithMemberSize container; | 
|  | EXPECT_EQ(range_size(container), 42u); | 
|  | } | 
|  |  | 
|  | struct FooWithFreeSize { | 
|  | friend size_t size(const FooWithFreeSize &) { return 13; } | 
|  | auto begin() { return Data.begin(); } | 
|  | auto end() { return Data.end(); } | 
|  |  | 
|  | std::set<int> Data; | 
|  | }; | 
|  |  | 
|  | TEST(RangeSizeTest, FreeSize) { | 
|  | // Make sure that `size(x)` is preferred over `std::distance`. | 
|  | FooWithFreeSize container; | 
|  | EXPECT_EQ(range_size(container), 13u); | 
|  | } | 
|  |  | 
|  | struct FooWithDistance { | 
|  | auto begin() { return Data.begin(); } | 
|  | auto end() { return Data.end(); } | 
|  |  | 
|  | std::set<int> Data; | 
|  | }; | 
|  |  | 
|  | TEST(RangeSizeTest, Distance) { | 
|  | // Make sure that we can fall back to `std::distance` even the iterator is not | 
|  | // random-access. | 
|  | FooWithDistance container; | 
|  | EXPECT_EQ(range_size(container), 0u); | 
|  | container.Data = {1, 2, 3, 4}; | 
|  | EXPECT_EQ(range_size(container), 4u); | 
|  | } | 
|  | } // anonymous namespace |