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

Few more cherry-picks to prep for reverse iterator changes #5386

Merged
merged 3 commits into from
Jul 3, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
48 changes: 48 additions & 0 deletions include/llvm/ADT/STLExtras.h
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,54 @@ inline mapped_iterator<ItTy, FuncTy> map_iterator(const ItTy &I, FuncTy F) {
return mapped_iterator<ItTy, FuncTy>(I, F);
}

/// Helper to determine if type T has a member called rbegin().
template <typename Ty> class has_rbegin_impl {
typedef char yes[1];
typedef char no[2];

template <typename Inner>
static yes& test(Inner *I, decltype(I->rbegin()) * = nullptr);

template <typename>
static no& test(...);

public:
static const bool value = sizeof(test<Ty>(nullptr)) == sizeof(yes);
};

/// Metafunction to determine if T& or T has a member called rbegin().
template <typename Ty>
struct has_rbegin : has_rbegin_impl<typename std::remove_reference<Ty>::type> {
};

// Returns an iterator_range over the given container which iterates in reverse.
// Note that the container must have rbegin()/rend() methods for this to work.
template <typename ContainerTy>
auto reverse(ContainerTy &&C,
typename std::enable_if<has_rbegin<ContainerTy>::value>::type * =
nullptr) -> decltype(make_range(C.rbegin(), C.rend())) {
return make_range(C.rbegin(), C.rend());
}

// Returns a std::reverse_iterator wrapped around the given iterator.
template <typename IteratorTy>
std::reverse_iterator<IteratorTy> make_reverse_iterator(IteratorTy It) {
return std::reverse_iterator<IteratorTy>(It);
}

// Returns an iterator_range over the given container which iterates in reverse.
// Note that the container must have begin()/end() methods which return
// bidirectional iterators for this to work.
template <typename ContainerTy>
auto reverse(
ContainerTy &&C,
typename std::enable_if<!has_rbegin<ContainerTy>::value>::type * = nullptr)
-> decltype(make_range(make_reverse_iterator(std::end(C)),
make_reverse_iterator(std::begin(C)))) {
return make_range(make_reverse_iterator(std::end(C)),
make_reverse_iterator(std::begin(C)));
}

//===----------------------------------------------------------------------===//
// Extra additions to <utility>
//===----------------------------------------------------------------------===//
Expand Down
4 changes: 1 addition & 3 deletions lib/CodeGen/AsmPrinter/ARMException.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,7 @@ void ARMException::emitTypeInfos(unsigned TTypeEncoding) {
Entry = TypeInfos.size();
}

for (std::vector<const GlobalValue *>::const_reverse_iterator
I = TypeInfos.rbegin(), E = TypeInfos.rend(); I != E; ++I) {
const GlobalValue *GV = *I;
for (const GlobalValue *GV : reverse(TypeInfos)) {
if (VerboseAsm)
Asm->OutStreamer->AddComment("TypeInfo " + Twine(Entry--));
Asm->EmitTTypeReference(GV, TTypeEncoding);
Expand Down
1 change: 1 addition & 0 deletions unittests/ADT/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ set(ADTSources
PointerIntPairTest.cpp
PointerUnionTest.cpp
PostOrderIteratorTest.cpp
RangeAdapterTest.cpp
SCCIteratorTest.cpp
SmallPtrSetTest.cpp
SmallStringTest.cpp
Expand Down
172 changes: 172 additions & 0 deletions unittests/ADT/RangeAdapterTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
//===- RangeAdapterTest.cpp - Unit tests for range adapters --------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "llvm/ADT/iterator_range.h"
#include "llvm/ADT/STLExtras.h"
#include "gtest/gtest.h"

#include <iterator>
#include <list>
#include <vector>

using namespace llvm;

namespace {

// A wrapper around vector which exposes rbegin(), rend().
class ReverseOnlyVector {
std::vector<int> Vec;

public:
ReverseOnlyVector(std::initializer_list<int> list) : Vec(list) {}

typedef std::vector<int>::reverse_iterator reverse_iterator;
typedef std::vector<int>::const_reverse_iterator const_reverse_iterator;
reverse_iterator rbegin() { return Vec.rbegin(); }
reverse_iterator rend() { return Vec.rend(); }
const_reverse_iterator rbegin() const { return Vec.rbegin(); }
const_reverse_iterator rend() const { return Vec.rend(); }
};

// A wrapper around vector which exposes begin(), end(), rbegin() and rend().
// begin() and end() don't have implementations as this ensures that we will
// get a linker error if reverse() chooses begin()/end() over rbegin(), rend().
class BidirectionalVector {
mutable std::vector<int> Vec;

public:
BidirectionalVector(std::initializer_list<int> list) : Vec(list) {}

typedef std::vector<int>::iterator iterator;
iterator begin() const;
iterator end() const;

typedef std::vector<int>::reverse_iterator reverse_iterator;
reverse_iterator rbegin() const { return Vec.rbegin(); }
reverse_iterator rend() const { return Vec.rend(); }
};

/// This is the same as BidirectionalVector but with the addition of const
/// begin/rbegin methods to ensure that the type traits for has_rbegin works.
class BidirectionalVectorConsts {
std::vector<int> Vec;

public:
BidirectionalVectorConsts(std::initializer_list<int> list) : Vec(list) {}

typedef std::vector<int>::iterator iterator;
typedef std::vector<int>::const_iterator const_iterator;
iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;

typedef std::vector<int>::reverse_iterator reverse_iterator;
typedef std::vector<int>::const_reverse_iterator const_reverse_iterator;
reverse_iterator rbegin() { return Vec.rbegin(); }
reverse_iterator rend() { return Vec.rend(); }
const_reverse_iterator rbegin() const { return Vec.rbegin(); }
const_reverse_iterator rend() const { return Vec.rend(); }
};

/// Check that types with custom iterators work.
class CustomIteratorVector {
mutable std::vector<int> V;

public:
CustomIteratorVector(std::initializer_list<int> list) : V(list) {}

typedef std::vector<int>::iterator iterator;
class reverse_iterator {
std::vector<int>::iterator I;

public:
reverse_iterator() = default;
reverse_iterator(const reverse_iterator &) = default;
reverse_iterator &operator=(const reverse_iterator &) = default;

explicit reverse_iterator(std::vector<int>::iterator I) : I(I) {}

reverse_iterator &operator++() {
--I;
return *this;
}
reverse_iterator &operator--() {
++I;
return *this;
}
int &operator*() const { return *std::prev(I); }
int *operator->() const { return &*std::prev(I); }
friend bool operator==(const reverse_iterator &L,
const reverse_iterator &R) {
return L.I == R.I;
}
friend bool operator!=(const reverse_iterator &L,
const reverse_iterator &R) {
return !(L == R);
}
};

iterator begin() const { return V.begin(); }
iterator end() const { return V.end(); }
reverse_iterator rbegin() const { return reverse_iterator(V.end()); }
reverse_iterator rend() const { return reverse_iterator(V.begin()); }
};

template <typename R> void TestRev(const R &r) {
int counter = 3;
for (int i : r)
EXPECT_EQ(i, counter--);
}

// Test fixture
template <typename T> class RangeAdapterLValueTest : public ::testing::Test {};

typedef ::testing::Types<std::vector<int>, std::list<int>, int[4]>
RangeAdapterLValueTestTypes;
TYPED_TEST_CASE(RangeAdapterLValueTest, RangeAdapterLValueTestTypes);

TYPED_TEST(RangeAdapterLValueTest, TrivialOperation) {
TypeParam v = {0, 1, 2, 3};
TestRev(reverse(v));

const TypeParam c = {0, 1, 2, 3};
TestRev(reverse(c));
}

template <typename T> struct RangeAdapterRValueTest : testing::Test {};

typedef ::testing::Types<std::vector<int>, std::list<int>, CustomIteratorVector,
ReverseOnlyVector, BidirectionalVector,
BidirectionalVectorConsts>
RangeAdapterRValueTestTypes;
TYPED_TEST_CASE(RangeAdapterRValueTest, RangeAdapterRValueTestTypes);

TYPED_TEST(RangeAdapterRValueTest, TrivialOperation) {
TestRev(reverse(TypeParam({0, 1, 2, 3})));
}

TYPED_TEST(RangeAdapterRValueTest, HasRbegin) {
static_assert(has_rbegin<TypeParam>::value, "rbegin() should be defined");
}

TYPED_TEST(RangeAdapterRValueTest, RangeType) {
static_assert(
std::is_same<
decltype(reverse(*static_cast<TypeParam *>(nullptr)).begin()),
decltype(static_cast<TypeParam *>(nullptr)->rbegin())>::value,
"reverse().begin() should have the same type as rbegin()");
static_assert(
std::is_same<
decltype(reverse(*static_cast<const TypeParam *>(nullptr)).begin()),
decltype(static_cast<const TypeParam *>(nullptr)->rbegin())>::value,
"reverse().begin() should have the same type as rbegin() [const]");
}

} // anonymous namespace