diff --git a/llvm/unittests/ADT/CMakeLists.txt b/llvm/unittests/ADT/CMakeLists.txt index 07568ad0c64e3..dafd73518aedb 100644 --- a/llvm/unittests/ADT/CMakeLists.txt +++ b/llvm/unittests/ADT/CMakeLists.txt @@ -67,6 +67,7 @@ add_llvm_unittest(ADTTests SCCIteratorTest.cpp STLExtrasTest.cpp STLForwardCompatTest.cpp + ScopedHashTableTest.cpp ScopeExitTest.cpp SequenceTest.cpp SetOperationsTest.cpp diff --git a/llvm/unittests/ADT/ScopedHashTableTest.cpp b/llvm/unittests/ADT/ScopedHashTableTest.cpp new file mode 100644 index 0000000000000..64afa948d9a17 --- /dev/null +++ b/llvm/unittests/ADT/ScopedHashTableTest.cpp @@ -0,0 +1,145 @@ +//===- ScopedHashTableTest.cpp - ScopedHashTable unit tests ---------------===// +// +// 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/ScopedHashTable.h" +#include "llvm/ADT/StringRef.h" +#include "gtest/gtest.h" +#include +#include + +using ::llvm::ScopedHashTable; +using ::llvm::ScopedHashTableScope; +using ::llvm::StringLiteral; +using ::llvm::StringRef; + +using ::testing::Test; + +class ScopedHashTableTest : public Test { +protected: + ScopedHashTableTest() { symbolTable.insert(kGlobalName, kGlobalValue); } + + ScopedHashTable symbolTable{}; + ScopedHashTableScope globalScope{symbolTable}; + + static constexpr StringLiteral kGlobalName = "global"; + static constexpr StringLiteral kGlobalValue = "gvalue"; + static constexpr StringLiteral kLocalName = "local"; + static constexpr StringLiteral kLocalValue = "lvalue"; + static constexpr StringLiteral kLocalValue2 = "lvalue2"; +}; + +TEST_F(ScopedHashTableTest, AccessWithNoActiveScope) { + EXPECT_EQ(symbolTable.count(kGlobalName), 1U); +} + +TEST_F(ScopedHashTableTest, AccessWithAScope) { + [[maybe_unused]] ScopedHashTableScope varScope(symbolTable); + EXPECT_EQ(symbolTable.count(kGlobalName), 1U); +} + +TEST_F(ScopedHashTableTest, InsertInScope) { + [[maybe_unused]] ScopedHashTableScope varScope(symbolTable); + symbolTable.insert(kLocalName, kLocalValue); + EXPECT_EQ(symbolTable.count(kLocalName), 1U); +} + +TEST_F(ScopedHashTableTest, InsertInLinearSortedScope) { + [[maybe_unused]] ScopedHashTableScope varScope(symbolTable); + [[maybe_unused]] ScopedHashTableScope varScope2(symbolTable); + [[maybe_unused]] ScopedHashTableScope varScope3(symbolTable); + symbolTable.insert(kLocalName, kLocalValue); + EXPECT_EQ(symbolTable.count(kLocalName), 1U); +} + +TEST_F(ScopedHashTableTest, InsertInOutedScope) { + { + [[maybe_unused]] ScopedHashTableScope varScope(symbolTable); + symbolTable.insert(kLocalName, kLocalValue); + } + EXPECT_EQ(symbolTable.count(kLocalName), 0U); +} + +TEST_F(ScopedHashTableTest, OverrideInScope) { + [[maybe_unused]] ScopedHashTableScope funScope(symbolTable); + symbolTable.insert(kLocalName, kLocalValue); + { + [[maybe_unused]] ScopedHashTableScope varScope(symbolTable); + symbolTable.insert(kLocalName, kLocalValue2); + EXPECT_EQ(symbolTable.lookup(kLocalName), kLocalValue2); + } + EXPECT_EQ(symbolTable.lookup(kLocalName), kLocalValue); +} + +TEST_F(ScopedHashTableTest, GetCurScope) { + EXPECT_EQ(symbolTable.getCurScope(), &globalScope); + { + ScopedHashTableScope funScope(symbolTable); + ScopedHashTableScope funScope2(symbolTable); + EXPECT_EQ(symbolTable.getCurScope(), &funScope2); + { + ScopedHashTableScope blockScope(symbolTable); + EXPECT_EQ(symbolTable.getCurScope(), &blockScope); + } + EXPECT_EQ(symbolTable.getCurScope(), &funScope2); + } + EXPECT_EQ(symbolTable.getCurScope(), &globalScope); +} + +TEST_F(ScopedHashTableTest, PopScope) { + using SymbolTableScopeTy = ScopedHashTable::ScopeTy; + + std::stack ExpectedValues; + std::stack> Scopes; + + Scopes.emplace(std::make_unique(symbolTable)); + ExpectedValues.emplace(kLocalValue); + symbolTable.insert(kGlobalName, kLocalValue); + + Scopes.emplace(std::make_unique(symbolTable)); + ExpectedValues.emplace(kLocalValue2); + symbolTable.insert(kGlobalName, kLocalValue2); + + while (symbolTable.getCurScope() != &globalScope) { + EXPECT_EQ(symbolTable.getCurScope(), Scopes.top().get()); + EXPECT_EQ(symbolTable.lookup(kGlobalName), ExpectedValues.top()); + ExpectedValues.pop(); + Scopes.pop(); // destructs the SymbolTableScopeTy instance implicitly + // calling Scopes.top()->~SymbolTableScopeTy(); + EXPECT_NE(symbolTable.getCurScope(), nullptr); + } + ASSERT_TRUE(ExpectedValues.empty()); + ASSERT_TRUE(Scopes.empty()); + EXPECT_EQ(symbolTable.lookup(kGlobalName), kGlobalValue); +} + +TEST_F(ScopedHashTableTest, DISABLED_PopScopeOnStack) { + using SymbolTableScopeTy = ScopedHashTable::ScopeTy; + SymbolTableScopeTy funScope(symbolTable); + symbolTable.insert(kGlobalName, kLocalValue); + SymbolTableScopeTy funScope2(symbolTable); + symbolTable.insert(kGlobalName, kLocalValue2); + + std::stack expectedValues{{kLocalValue, kLocalValue2}}; + std::stack expectedScopes{{&funScope, &funScope2}}; + + while (symbolTable.getCurScope() != &globalScope) { + EXPECT_EQ(symbolTable.getCurScope(), expectedScopes.top()); + expectedScopes.pop(); + EXPECT_EQ(symbolTable.lookup(kGlobalName), expectedValues.top()); + expectedValues.pop(); + symbolTable.getCurScope()->~SymbolTableScopeTy(); + EXPECT_NE(symbolTable.getCurScope(), nullptr); + } + + // We have imbalanced scopes here: + // Assertion `HT.CurScope == this && "Scope imbalance!"' failed + // HT.CurScope is a pointer to the `globalScope` while + // `SymbolTableScopeTy.this` is still a pointer to `funScope2`. + // There is no way to write an assert on an assert in googletest so that we + // mark the test case as DISABLED. +}