forked from OSchip/llvm-project
246 lines
6.8 KiB
C++
246 lines
6.8 KiB
C++
//===- lld/unittest/RangeTest.cpp -----------------------------------------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
///
|
|
/// \file
|
|
/// \brief range.h unit tests.
|
|
///
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "gtest/gtest.h"
|
|
|
|
#include "lld/Core/range.h"
|
|
|
|
#include <assert.h>
|
|
#include <array>
|
|
#include <deque>
|
|
#include <forward_list>
|
|
#include <iterator>
|
|
#include <list>
|
|
#include <numeric>
|
|
#include <sstream>
|
|
#include <vector>
|
|
|
|
template <typename T, typename U> struct AssertTypesSame;
|
|
template <typename T> struct AssertTypesSame<T, T> {};
|
|
#define ASSERT_TYPES_SAME(T, U) AssertTypesSame<T, U>()
|
|
|
|
struct no_begin {};
|
|
struct member_begin {
|
|
int *begin();
|
|
};
|
|
struct free_begin {};
|
|
int *begin(free_begin);
|
|
|
|
template <typename T>
|
|
auto type_of_forward(T &&t) -> decltype(std::forward<T>(t)) {
|
|
return std::forward<T>(t);
|
|
}
|
|
|
|
template <typename To> To implicit_cast(To val) { return val; }
|
|
|
|
void test_traits() {
|
|
using namespace lld::detail;
|
|
ASSERT_TYPES_SAME(begin_result<no_begin>::type, undefined);
|
|
// This causes clang to segfault.
|
|
#if 0
|
|
ASSERT_TYPES_SAME(
|
|
begin_result<decltype(type_of_forward(member_begin()))>::type, int *);
|
|
#endif
|
|
ASSERT_TYPES_SAME(begin_result<free_begin>::type, int *);
|
|
}
|
|
|
|
TEST(Range, constructors) {
|
|
std::vector<int> v(5);
|
|
std::iota(v.begin(), v.end(), 0);
|
|
lld::range<std::vector<int>::iterator> r = v;
|
|
EXPECT_EQ(v.begin(), r.begin());
|
|
EXPECT_EQ(v.end(), r.end());
|
|
|
|
int arr[] = { 1, 2, 3, 4, 5 };
|
|
std::begin(arr);
|
|
lld::range<int *> r2 = arr;
|
|
EXPECT_EQ(5, r2.back());
|
|
}
|
|
|
|
TEST(Range, conversion_to_pointer_range) {
|
|
std::vector<int> v(5);
|
|
std::iota(v.begin(), v.end(), 0);
|
|
lld::range<int *> r = v;
|
|
EXPECT_EQ(&*v.begin(), r.begin());
|
|
EXPECT_EQ(2, r[2]);
|
|
}
|
|
|
|
template <typename Iter> void takes_range(lld::range<Iter> r) {
|
|
int expected = 0;
|
|
for (int val : r) {
|
|
EXPECT_EQ(expected++, val);
|
|
}
|
|
}
|
|
|
|
void takes_ptr_range(lld::range<const int *> r) {
|
|
int expected = 0;
|
|
for (int val : r) {
|
|
EXPECT_EQ(expected++, val);
|
|
}
|
|
}
|
|
|
|
TEST(Range, passing) {
|
|
using lld::make_range;
|
|
using lld::make_ptr_range;
|
|
std::list<int> l(5);
|
|
std::iota(l.begin(), l.end(), 0);
|
|
takes_range(make_range(l));
|
|
takes_range(make_range(implicit_cast<const std::list<int> &>(l)));
|
|
std::deque<int> d(5);
|
|
std::iota(d.begin(), d.end(), 0);
|
|
takes_range(make_range(d));
|
|
takes_range(make_range(implicit_cast<const std::deque<int> &>(d)));
|
|
std::vector<int> v(5);
|
|
std::iota(v.begin(), v.end(), 0);
|
|
takes_range(make_range(v));
|
|
takes_range(make_range(implicit_cast<const std::vector<int> &>(v)));
|
|
// MSVC Can't compile make_ptr_range.
|
|
#ifndef _MSC_VER
|
|
static_assert(
|
|
std::is_same<decltype(make_ptr_range(v)), lld::range<int *>>::value,
|
|
"make_ptr_range should return a range of pointers");
|
|
takes_range(make_ptr_range(v));
|
|
takes_range(make_ptr_range(implicit_cast<const std::vector<int> &>(v)));
|
|
#endif
|
|
int arr[] = { 0, 1, 2, 3, 4 };
|
|
takes_range(make_range(arr));
|
|
const int carr[] = { 0, 1, 2, 3, 4 };
|
|
takes_range(make_range(carr));
|
|
|
|
takes_ptr_range(v);
|
|
takes_ptr_range(implicit_cast<const std::vector<int> &>(v));
|
|
takes_ptr_range(arr);
|
|
takes_ptr_range(carr);
|
|
}
|
|
|
|
TEST(Range, access) {
|
|
std::array<int, 5> a = { { 1, 2, 3, 4, 5 } };
|
|
lld::range<decltype(a.begin())> r = a;
|
|
EXPECT_EQ(4, r[3]);
|
|
EXPECT_EQ(4, r[-2]);
|
|
}
|
|
|
|
template <bool b> struct CompileAssert;
|
|
template <> struct CompileAssert<true> {};
|
|
|
|
#if __has_feature(cxx_constexpr)
|
|
constexpr int arr[] = { 1, 2, 3, 4, 5 };
|
|
TEST(Range, constexpr) {
|
|
constexpr lld::range<const int *> r(arr, arr + 5);
|
|
CompileAssert<r.front() == 1>();
|
|
CompileAssert<r.size() == 5>();
|
|
CompileAssert<r[4] == 5>();
|
|
}
|
|
#endif
|
|
|
|
template <typename Container> void test_slice() {
|
|
Container cont(10);
|
|
std::iota(cont.begin(), cont.end(), 0);
|
|
lld::range<decltype(cont.begin())> r = cont;
|
|
|
|
// One argument.
|
|
EXPECT_EQ(10, r.slice(0).size());
|
|
EXPECT_EQ(8, r.slice(2).size());
|
|
EXPECT_EQ(2, r.slice(2).front());
|
|
EXPECT_EQ(1, r.slice(-1).size());
|
|
EXPECT_EQ(9, r.slice(-1).front());
|
|
|
|
// Two positive arguments.
|
|
EXPECT_TRUE(r.slice(5, 2).empty());
|
|
EXPECT_EQ(next(cont.begin(), 5), r.slice(5, 2).begin());
|
|
EXPECT_EQ(1, r.slice(1, 2).size());
|
|
EXPECT_EQ(1, r.slice(1, 2).front());
|
|
|
|
// Two negative arguments.
|
|
EXPECT_TRUE(r.slice(-2, -5).empty());
|
|
EXPECT_EQ(next(cont.begin(), 8), r.slice(-2, -5).begin());
|
|
EXPECT_EQ(1, r.slice(-2, -1).size());
|
|
EXPECT_EQ(8, r.slice(-2, -1).front());
|
|
|
|
// Positive start, negative stop.
|
|
EXPECT_EQ(1, r.slice(6, -3).size());
|
|
EXPECT_EQ(6, r.slice(6, -3).front());
|
|
EXPECT_TRUE(r.slice(6, -5).empty());
|
|
EXPECT_EQ(next(cont.begin(), 6), r.slice(6, -5).begin());
|
|
|
|
// Negative start, positive stop.
|
|
EXPECT_TRUE(r.slice(-3, 6).empty());
|
|
EXPECT_EQ(next(cont.begin(), 7), r.slice(-3, 6).begin());
|
|
EXPECT_EQ(1, r.slice(-5, 6).size());
|
|
EXPECT_EQ(5, r.slice(-5, 6).front());
|
|
}
|
|
|
|
TEST(Range, slice) {
|
|
// -fsanitize=undefined complains about this, but only if optimizations are
|
|
// enabled.
|
|
#if 0
|
|
test_slice<std::forward_list<int>>();
|
|
#endif
|
|
test_slice<std::list<int>>();
|
|
// This doesn't build with libstdc++ 4.7
|
|
#if 0
|
|
test_slice<std::deque<int>>();
|
|
#endif
|
|
}
|
|
|
|
// This test is flaky and I've yet to pin down why. Changing between
|
|
// EXPECT_EQ(1, input.front()) and EXPECT_TRUE(input.front() == 1) makes it work
|
|
// with VS 2012 in Debug mode. Clang on Linux seems to fail with -03 and -02 -g
|
|
// -fsanitize=undefined.
|
|
#if 0
|
|
TEST(Range, istream_range) {
|
|
std::istringstream stream("1 2 3 4 5");
|
|
// MSVC interprets input as a function declaration if you don't declare start
|
|
// and instead directly pass std::istream_iterator<int>(stream).
|
|
auto start = std::istream_iterator<int>(stream);
|
|
lld::range<std::istream_iterator<int>> input(
|
|
start, std::istream_iterator<int>());
|
|
EXPECT_TRUE(input.front() == 1);
|
|
input.pop_front();
|
|
EXPECT_TRUE(input.front() == 2);
|
|
input.pop_front(2);
|
|
EXPECT_TRUE(input.front() == 4);
|
|
input.pop_front_upto(7);
|
|
EXPECT_TRUE(input.empty());
|
|
}
|
|
#endif
|
|
|
|
//! [algorithm using range]
|
|
template <typename T> void partial_sum(T &container) {
|
|
using lld::make_range;
|
|
auto range = make_range(container);
|
|
typename T::value_type sum = 0;
|
|
// One would actually use a range-based for loop
|
|
// in this case, but you get the idea:
|
|
for (; !range.empty(); range.pop_front()) {
|
|
sum += range.front();
|
|
range.front() = sum;
|
|
}
|
|
}
|
|
|
|
TEST(Range, user1) {
|
|
std::vector<int> v(5, 2);
|
|
partial_sum(v);
|
|
EXPECT_EQ(8, v[3]);
|
|
}
|
|
//! [algorithm using range]
|
|
|
|
//! [algorithm using ptr_range]
|
|
void my_write(int fd, lld::range<const char *> buffer) {}
|
|
|
|
TEST(Range, user2) {
|
|
std::string s("Hello world");
|
|
my_write(1, s);
|
|
}
|