diff options
author | Duncan P. N. Exon Smith <dexonsmith@apple.com> | 2020-11-13 18:29:32 -0500 |
---|---|---|
committer | Duncan P. N. Exon Smith <dexonsmith@apple.com> | 2020-11-18 17:36:28 -0800 |
commit | 5abf76fbe37380874a88cc9aa02164800e4e10f3 (patch) | |
tree | 88c4452263ad661ab0f5908da6ba2bca5c8eff56 /llvm/unittests/ADT/SmallVectorTest.cpp | |
parent | 9ad62f62b9ad9852fea17a4c81b35e281e45fbaf (diff) | |
download | llvm-5abf76fbe37380874a88cc9aa02164800e4e10f3.zip llvm-5abf76fbe37380874a88cc9aa02164800e4e10f3.tar.gz llvm-5abf76fbe37380874a88cc9aa02164800e4e10f3.tar.bz2 |
ADT: Add assertions to SmallVector::insert, etc., for reference invalidation
2c196bbc6bd897b3dcc1d87a3baac28e1e88df41 asserted that
`SmallVector::push_back` doesn't invalidate the parameter when it needs
to grow. Do the same for `resize`, `append`, `assign`, `insert`, and
`emplace_back`.
Differential Revision: https://reviews.llvm.org/D91744
Diffstat (limited to 'llvm/unittests/ADT/SmallVectorTest.cpp')
-rw-r--r-- | llvm/unittests/ADT/SmallVectorTest.cpp | 198 |
1 files changed, 197 insertions, 1 deletions
diff --git a/llvm/unittests/ADT/SmallVectorTest.cpp b/llvm/unittests/ADT/SmallVectorTest.cpp index 162716a..1a61d23 100644 --- a/llvm/unittests/ADT/SmallVectorTest.cpp +++ b/llvm/unittests/ADT/SmallVectorTest.cpp @@ -252,7 +252,9 @@ TYPED_TEST(SmallVectorTest, PushPopTest) { this->theVector.push_back(Constructable(2)); this->assertValuesInOrder(this->theVector, 2u, 1, 2); - // Insert at beginning + // Insert at beginning. Reserve space to avoid reference invalidation from + // this->theVector[1]. + this->theVector.reserve(this->theVector.size() + 1); this->theVector.insert(this->theVector.begin(), this->theVector[1]); this->assertValuesInOrder(this->theVector, 3u, 2, 1, 2); @@ -999,4 +1001,198 @@ TEST(SmallVectorTest, InitializerList) { EXPECT_TRUE(makeArrayRef(V2).equals({4, 5, 3, 2})); } +template <class VectorT> +class SmallVectorReferenceInvalidationTest : public SmallVectorTestBase { +protected: + const char *AssertionMessage = + "Attempting to reference an element of the vector in an operation \" " + "\"that invalidates it"; + + VectorT V; + + template <typename T, unsigned N> + static unsigned NumBuiltinElts(const SmallVector<T, N> &) { + return N; + } + + void SetUp() override { + SmallVectorTestBase::SetUp(); + + // Fill up the small size so that insertions move the elements. + V.append({0, 0, 0}); + } +}; + +// Test one type that's trivially copyable (int) and one that isn't +// (Constructable) since reference invalidation may be fixed differently for +// each. +using SmallVectorReferenceInvalidationTestTypes = + ::testing::Types<SmallVector<int, 3>, SmallVector<Constructable, 3>>; + +TYPED_TEST_CASE(SmallVectorReferenceInvalidationTest, + SmallVectorReferenceInvalidationTestTypes); + +TYPED_TEST(SmallVectorReferenceInvalidationTest, PushBack) { + auto &V = this->V; + (void)V; +#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST + EXPECT_DEATH(V.push_back(V.back()), this->AssertionMessage); +#endif +} + +TYPED_TEST(SmallVectorReferenceInvalidationTest, PushBackMoved) { + auto &V = this->V; + (void)V; +#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST + EXPECT_DEATH(V.push_back(std::move(V.back())), this->AssertionMessage); +#endif +} + +TYPED_TEST(SmallVectorReferenceInvalidationTest, Resize) { + auto &V = this->V; + (void)V; +#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST + EXPECT_DEATH(V.resize(2, V.back()), this->AssertionMessage); +#endif +} + +TYPED_TEST(SmallVectorReferenceInvalidationTest, Append) { + auto &V = this->V; + (void)V; +#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST + EXPECT_DEATH(V.append(1, V.back()), this->AssertionMessage); +#endif +} + +TYPED_TEST(SmallVectorReferenceInvalidationTest, AppendRange) { + auto &V = this->V; + (void)V; +#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST + EXPECT_DEATH(V.append(V.begin(), V.begin() + 1), this->AssertionMessage); + + ASSERT_EQ(3u, this->NumBuiltinElts(V)); + ASSERT_EQ(3u, V.size()); + V.pop_back(); + ASSERT_EQ(2u, V.size()); + + // Confirm this checks for growth when there's more than one element + // appended. + EXPECT_DEATH(V.append(V.begin(), V.end()), this->AssertionMessage); +#endif +} + +TYPED_TEST(SmallVectorReferenceInvalidationTest, Assign) { + auto &V = this->V; + (void)V; +#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST + // Regardless of capacity, assign should never reference an internal element. + EXPECT_DEATH(V.assign(1, V.back()), this->AssertionMessage); + EXPECT_DEATH(V.assign(this->NumBuiltinElts(V), V.back()), + this->AssertionMessage); + EXPECT_DEATH(V.assign(this->NumBuiltinElts(V) + 1, V.back()), + this->AssertionMessage); +#endif +} + +TYPED_TEST(SmallVectorReferenceInvalidationTest, AssignRange) { + auto &V = this->V; +#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST + EXPECT_DEATH(V.assign(V.begin(), V.end()), this->AssertionMessage); + EXPECT_DEATH(V.assign(V.begin(), V.end() - 1), this->AssertionMessage); +#endif + V.assign(V.begin(), V.begin()); + EXPECT_TRUE(V.empty()); +} + +TYPED_TEST(SmallVectorReferenceInvalidationTest, Insert) { + auto &V = this->V; + (void)V; +#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST + EXPECT_DEATH(V.insert(V.begin(), V.back()), this->AssertionMessage); +#endif +} + +TYPED_TEST(SmallVectorReferenceInvalidationTest, InsertMoved) { + auto &V = this->V; + (void)V; +#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST + EXPECT_DEATH(V.insert(V.begin(), std::move(V.back())), + this->AssertionMessage); +#endif +} + +TYPED_TEST(SmallVectorReferenceInvalidationTest, InsertN) { + auto &V = this->V; + (void)V; +#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST + EXPECT_DEATH(V.insert(V.begin(), 2, V.back()), this->AssertionMessage); +#endif +} + +TYPED_TEST(SmallVectorReferenceInvalidationTest, InsertRange) { + auto &V = this->V; + (void)V; +#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST + EXPECT_DEATH(V.insert(V.begin(), V.begin(), V.begin() + 1), + this->AssertionMessage); + + ASSERT_EQ(3u, this->NumBuiltinElts(V)); + ASSERT_EQ(3u, V.size()); + V.pop_back(); + ASSERT_EQ(2u, V.size()); + + // Confirm this checks for growth when there's more than one element + // inserted. + EXPECT_DEATH(V.insert(V.begin(), V.begin(), V.end()), this->AssertionMessage); +#endif +} + +TYPED_TEST(SmallVectorReferenceInvalidationTest, EmplaceBack) { + auto &V = this->V; + (void)V; +#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST + EXPECT_DEATH(V.emplace_back(V.back()), this->AssertionMessage); +#endif +} + +template <class VectorT> +class SmallVectorInternalReferenceInvalidationTest + : public SmallVectorTestBase { +protected: + const char *AssertionMessage = + "Attempting to reference an element of the vector in an operation \" " + "\"that invalidates it"; + + VectorT V; + + template <typename T, unsigned N> + static unsigned NumBuiltinElts(const SmallVector<T, N> &) { + return N; + } + + void SetUp() override { + SmallVectorTestBase::SetUp(); + + // Fill up the small size so that insertions move the elements. + V.push_back(std::make_pair(0, 0)); + } +}; + +// Test pairs of the same types from SmallVectorReferenceInvalidationTestTypes. +using SmallVectorInternalReferenceInvalidationTestTypes = + ::testing::Types<SmallVector<std::pair<int, int>, 1>, + SmallVector<std::pair<Constructable, Constructable>, 1>>; + +TYPED_TEST_CASE(SmallVectorInternalReferenceInvalidationTest, + SmallVectorInternalReferenceInvalidationTestTypes); + +TYPED_TEST(SmallVectorInternalReferenceInvalidationTest, EmplaceBack) { + auto &V = this->V; + (void)V; +#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST + EXPECT_DEATH(V.emplace_back(V.back().first, 0), this->AssertionMessage); + EXPECT_DEATH(V.emplace_back(0, V.back().second), this->AssertionMessage); +#endif +} + } // end namespace |