llvm/unittests/ADT/BumpPtrListTest.cpp
Duncan P. N. Exon Smith 5de5554390 ADT: Add AllocatorList, and use it for yaml::Token
- Add AllocatorList, a non-intrusive list that owns an LLVM-style
  allocator and provides a std::list-like interface (trivially built on
  top of simple_ilist),
- add a typedef (and unit tests) for BumpPtrList, and
- use BumpPtrList for the list of llvm::yaml::Token (i.e., TokenQueueT).

TokenQueueT has no need for the complexity of an intrusive list.  The
only reason to inherit from ilist was to customize the allocator.
TokenQueueT was the only example in-tree of using ilist<> in a truly
non-intrusive way.

Moreover, this removes the final use of the non-intrusive
ilist_traits<>::createNode (after r280573, r281177, and r281181).  I
have a WIP patch that removes this customization point (and the API that
relies on it) that I plan to commit soon.

Note: AllocatorList owns the allocator, which limits the viable API
(e.g., splicing must be on the same list).  For now I've left out
any problematic API.  It wouldn't be hard to split AllocatorList into
two layers: an Impl class that calls DerivedT::getAlloc (via CRTP), and
derived classes that handle Allocator ownership/reference/etc semantics;
and then implement splice with appropriate assertions; but TBH we should
probably just customize the std::list allocators at that point.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@281182 91177308-0d34-0410-b5e6-96231b3b80d8
2016-09-11 22:40:40 +00:00

244 lines
5.8 KiB
C++

//===- unittests/ADT/BumpPtrListTest.cpp - BumpPtrList unit tests ---------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "llvm/ADT/AllocatorList.h"
#include "llvm/ADT/STLExtras.h"
#include "gtest/gtest.h"
using namespace llvm;
namespace {
struct CountsDestructors {
static unsigned NumCalls;
~CountsDestructors() { ++NumCalls; }
};
unsigned CountsDestructors::NumCalls = 0;
struct MoveOnly {
int V;
explicit MoveOnly(int V) : V(V) {}
MoveOnly() = delete;
MoveOnly(MoveOnly &&X) { V = X.V; }
MoveOnly(const MoveOnly &X) = delete;
MoveOnly &operator=(MoveOnly &&X) = delete;
MoveOnly &operator=(const MoveOnly &X) = delete;
};
struct EmplaceOnly {
int V1, V2;
explicit EmplaceOnly(int V1, int V2) : V1(V1), V2(V2) {}
EmplaceOnly() = delete;
EmplaceOnly(EmplaceOnly &&X) = delete;
EmplaceOnly(const EmplaceOnly &X) = delete;
EmplaceOnly &operator=(EmplaceOnly &&X) = delete;
EmplaceOnly &operator=(const EmplaceOnly &X) = delete;
};
TEST(BumpPtrListTest, DefaultConstructor) {
BumpPtrList<int> L;
EXPECT_TRUE(L.empty());
}
TEST(BumpPtrListTest, pushPopBack) {
// Build a list with push_back.
BumpPtrList<int> L;
int Ns[] = {1, 3, 9, 5, 7};
for (const int N : Ns)
L.push_back(N);
// Use iterators to check contents.
auto I = L.begin();
for (int N : Ns)
EXPECT_EQ(N, *I++);
EXPECT_EQ(I, L.end());
// Unbuild the list with pop_back.
for (int N : llvm::reverse(Ns)) {
EXPECT_EQ(N, L.back());
L.pop_back();
}
EXPECT_TRUE(L.empty());
}
TEST(BumpPtrListTest, pushPopFront) {
// Build a list with push_front.
BumpPtrList<int> L;
int Ns[] = {1, 3, 9, 5, 7};
for (const int N : Ns)
L.push_front(N);
// Use reverse iterators to check contents.
auto I = L.rbegin();
for (int N : Ns)
EXPECT_EQ(N, *I++);
EXPECT_EQ(I, L.rend());
// Unbuild the list with pop_front.
for (int N : llvm::reverse(Ns)) {
EXPECT_EQ(N, L.front());
L.pop_front();
}
EXPECT_TRUE(L.empty());
}
TEST(BumpPtrListTest, pushBackMoveOnly) {
BumpPtrList<MoveOnly> L;
int Ns[] = {1, 3, 9, 5, 7};
for (const int N : Ns) {
L.push_back(MoveOnly(N));
EXPECT_EQ(N, L.back().V);
}
// Instantiate with MoveOnly.
while (!L.empty())
L.pop_back();
}
TEST(BumpPtrListTest, pushFrontMoveOnly) {
BumpPtrList<MoveOnly> L;
int Ns[] = {1, 3, 9, 5, 7};
for (const int N : Ns) {
L.push_front(MoveOnly(N));
EXPECT_EQ(N, L.front().V);
}
// Instantiate with MoveOnly.
while (!L.empty())
L.pop_front();
}
TEST(BumpPtrListTest, emplaceBack) {
BumpPtrList<EmplaceOnly> L;
int N1s[] = {1, 3, 9, 5, 7};
int N2s[] = {7, 3, 1, 8, 2};
for (int I = 0; I != 5; ++I) {
L.emplace_back(N1s[I], N2s[I]);
EXPECT_EQ(N1s[I], L.back().V1);
EXPECT_EQ(N2s[I], L.back().V2);
}
// Instantiate with EmplaceOnly.
while (!L.empty())
L.pop_back();
}
TEST(BumpPtrListTest, emplaceFront) {
BumpPtrList<EmplaceOnly> L;
int N1s[] = {1, 3, 9, 5, 7};
int N2s[] = {7, 3, 1, 8, 2};
for (int I = 0; I != 5; ++I) {
L.emplace_front(N1s[I], N2s[I]);
EXPECT_EQ(N1s[I], L.front().V1);
EXPECT_EQ(N2s[I], L.front().V2);
}
// Instantiate with EmplaceOnly.
while (!L.empty())
L.pop_front();
}
TEST(BumpPtrListTest, swap) {
// Build two lists with different lifetimes and swap them.
int N1s[] = {1, 3, 5, 7, 9};
int N2s[] = {2, 4, 6, 8, 10};
BumpPtrList<int> L1;
L1.insert(L1.end(), std::begin(N1s), std::end(N1s));
{
BumpPtrList<int> L2;
L2.insert(L2.end(), std::begin(N2s), std::end(N2s));
// Swap the lists.
L1.swap(L2);
// Check L2's contents before it goes out of scope.
auto I = L2.begin();
for (int N : N1s)
EXPECT_EQ(N, *I++);
EXPECT_EQ(I, L2.end());
}
// Check L1's contents now that L2 is out of scope (with its allocation
// blocks).
auto I = L1.begin();
for (int N : N2s)
EXPECT_EQ(N, *I++);
EXPECT_EQ(I, L1.end());
}
TEST(BumpPtrListTest, clear) {
CountsDestructors::NumCalls = 0;
CountsDestructors N;
BumpPtrList<CountsDestructors> L;
L.push_back(N);
L.push_back(N);
L.push_back(N);
EXPECT_EQ(3u, L.size());
EXPECT_EQ(0u, CountsDestructors::NumCalls);
L.pop_back();
EXPECT_EQ(1u, CountsDestructors::NumCalls);
L.clear();
EXPECT_EQ(3u, CountsDestructors::NumCalls);
}
TEST(BumpPtrListTest, move) {
BumpPtrList<int> L1, L2;
L1.push_back(1);
L2.push_back(2);
L1 = std::move(L2);
EXPECT_EQ(1u, L1.size());
EXPECT_EQ(2, L1.front());
EXPECT_EQ(0u, L2.size());
}
TEST(BumpPtrListTest, moveCallsDestructors) {
CountsDestructors::NumCalls = 0;
BumpPtrList<CountsDestructors> L1, L2;
L1.emplace_back();
EXPECT_EQ(0u, CountsDestructors::NumCalls);
L1 = std::move(L2);
EXPECT_EQ(1u, CountsDestructors::NumCalls);
}
TEST(BumpPtrListTest, copy) {
BumpPtrList<int> L1, L2;
L1.push_back(1);
L2.push_back(2);
L1 = L2;
EXPECT_EQ(1u, L1.size());
EXPECT_EQ(2, L1.front());
EXPECT_EQ(1u, L2.size());
EXPECT_EQ(2, L2.front());
}
TEST(BumpPtrListTest, copyCallsDestructors) {
CountsDestructors::NumCalls = 0;
BumpPtrList<CountsDestructors> L1, L2;
L1.emplace_back();
EXPECT_EQ(0u, CountsDestructors::NumCalls);
L1 = L2;
EXPECT_EQ(1u, CountsDestructors::NumCalls);
}
TEST(BumpPtrListTest, resetAlloc) {
// Resetting an empty list should work.
BumpPtrList<int> L;
// Resetting an empty list that has allocated should also work.
L.resetAlloc();
L.push_back(5);
L.erase(L.begin());
L.resetAlloc();
// Resetting a non-empty list should crash.
L.push_back(5);
#if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG)
EXPECT_DEATH(L.resetAlloc(), "Cannot reset allocator if not empty");
#endif
}
} // end namespace