From ab0998b0e87eb153be88faf4d2731a54bdfd028a Mon Sep 17 00:00:00 2001 From: MikePopoloski Date: Sun, 25 Jun 2023 08:26:02 -0400 Subject: [PATCH 1/4] Unit tests and fixes for SmallVector --- include/slang/util/SmallVector.h | 39 +- tests/CMakeLists.txt | 1 + tests/unittests/SmallVectorTests.cpp | 1279 ++++++++++++++++++++++++++ 3 files changed, 1306 insertions(+), 13 deletions(-) create mode 100644 tests/unittests/SmallVectorTests.cpp diff --git a/include/slang/util/SmallVector.h b/include/slang/util/SmallVector.h index b3baa3308..efcd250e4 100644 --- a/include/slang/util/SmallVector.h +++ b/include/slang/util/SmallVector.h @@ -172,7 +172,7 @@ class SmallVectorBase { } /// Appends a range of elements to the end of the array. - template>> + template void append(TIter first, TIter last) { auto numElems = static_cast(std::ranges::distance(first, last)); auto newSize = len + numElems; @@ -231,6 +231,12 @@ class SmallVectorBase { append(first, last); } + /// Resets the contents of the array to be the contents of the given range. + template + void assign(const TContainer& container) { + assign(std::ranges::begin(container), std::ranges::end(container)); + } + /// Constructs a new element at the specified position in the array. template iterator emplace(const_iterator pos, Args&&... args) { @@ -280,33 +286,32 @@ class SmallVectorBase { reserve(newSize); // Reset the iterator since reserve() may have invalidated it. - pos = begin() + offset; + auto result = begin() + offset; // If there are more existing elements between the insertion point and // the end of the range than there are being inserted we can use a // simpler approach for insertion. - auto existingOverlap = static_cast(end() - pos); + auto existingOverlap = static_cast(end() - result); if (existingOverlap >= numElems) { auto oldEnd = end(); append(std::move_iterator(end() - numElems), std::move_iterator(end())); - std::ranges::move_backward(pos, oldEnd - numElems, oldEnd); - std::ranges::copy(first, last, pos); - return pos; + std::ranges::move_backward(result, oldEnd - numElems, oldEnd); + std::ranges::copy(first, last, result); + return result; } // Move over elements we're about to overwrite. - std::uninitialized_move(pos, end(), begin() + newSize - existingOverlap); + std::uninitialized_move(result, end(), begin() + newSize - existingOverlap); // Copy in the new elements. - std::ranges::copy_n(first, existingOverlap, pos); - first += existingOverlap; + first = std::ranges::copy_n(first, existingOverlap, result).in; - // Insert the non-overwritten middle part. - std::ranges::uninitialized_copy(first, last, end()); + // Insert the non-overwritten end part. + std::ranges::uninitialized_copy(first, last, end(), end() + numElems - existingOverlap); len = newSize; - return pos; + return result; } /// Inserts @a count copies of @a value at the specified position in the array. @@ -490,7 +495,9 @@ class SmallVectorBase { if (newSize > len) { if (newSize > cap) { - resizeRealloc(newSize, val); + // Copy the value in case it's inside our existing array. + TVal temp(val); + resizeRealloc(newSize, temp); return; } @@ -573,6 +580,12 @@ class SmallVector : public SmallVectorBase { this->append(first, last); } + /// Constructs the SmallVector from the given range. + template + explicit SmallVector(TRange range) { + this->append(range); + } + /// Copy constructs from another vector. SmallVector(const SmallVector& other) : SmallVector(static_cast(other)) {} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b30b210e5..5a3743dbb 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -27,6 +27,7 @@ add_executable( unittests/PortTests.cpp unittests/PreprocessorTests.cpp unittests/PrimitiveTests.cpp + unittests/SmallVectorTests.cpp unittests/StatementParsingTests.cpp unittests/StatementTests.cpp unittests/SubroutineTests.cpp diff --git a/tests/unittests/SmallVectorTests.cpp b/tests/unittests/SmallVectorTests.cpp new file mode 100644 index 000000000..b73f9ce21 --- /dev/null +++ b/tests/unittests/SmallVectorTests.cpp @@ -0,0 +1,1279 @@ +// SPDX-FileCopyrightText: Michael Popoloski +// SPDX-License-Identifier: MIT + +#include "Test.h" +#include +#include + +#include "slang/util/SmallVector.h" + +namespace { + +// A helper class that counts the total number of constructor and +// destructor calls. +class Constructable { +private: + static int numConstructorCalls; + static int numMoveConstructorCalls; + static int numCopyConstructorCalls; + static int numDestructorCalls; + static int numAssignmentCalls; + static int numMoveAssignmentCalls; + static int numCopyAssignmentCalls; + + bool constructed; + int value; + +public: + Constructable() : constructed(true), value(0) { ++numConstructorCalls; } + + Constructable(int val) : constructed(true), value(val) { ++numConstructorCalls; } + + Constructable(const Constructable& src) : constructed(true) { + value = src.value; + ++numConstructorCalls; + ++numCopyConstructorCalls; + } + + Constructable(Constructable&& src) : constructed(true) { + value = src.value; + src.value = 0; + ++numConstructorCalls; + ++numMoveConstructorCalls; + } + + ~Constructable() { + CHECK(constructed); + ++numDestructorCalls; + constructed = false; + } + + Constructable& operator=(const Constructable& src) { + CHECK(constructed); + value = src.value; + ++numAssignmentCalls; + ++numCopyAssignmentCalls; + return *this; + } + + Constructable& operator=(Constructable&& src) { + CHECK(constructed); + value = src.value; + src.value = 0; + ++numAssignmentCalls; + ++numMoveAssignmentCalls; + return *this; + } + + int getValue() const { return abs(value); } + + static void reset() { + numConstructorCalls = 0; + numMoveConstructorCalls = 0; + numCopyConstructorCalls = 0; + numDestructorCalls = 0; + numAssignmentCalls = 0; + numMoveAssignmentCalls = 0; + numCopyAssignmentCalls = 0; + } + + static int getNumConstructorCalls() { return numConstructorCalls; } + static int getNumMoveConstructorCalls() { return numMoveConstructorCalls; } + static int getNumCopyConstructorCalls() { return numCopyConstructorCalls; } + static int getNumDestructorCalls() { return numDestructorCalls; } + static int getNumAssignmentCalls() { return numAssignmentCalls; } + static int getNumMoveAssignmentCalls() { return numMoveAssignmentCalls; } + static int getNumCopyAssignmentCalls() { return numCopyAssignmentCalls; } + + bool operator==(const Constructable& other) const { return getValue() == other.getValue(); } + + std::strong_ordering operator<=>(const Constructable& other) const { + return getValue() <=> other.getValue(); + } +}; + +int Constructable::numConstructorCalls; +int Constructable::numMoveConstructorCalls; +int Constructable::numCopyConstructorCalls; +int Constructable::numDestructorCalls; +int Constructable::numAssignmentCalls; +int Constructable::numMoveAssignmentCalls; +int Constructable::numCopyAssignmentCalls; + +template +void assertValuesInOrder(TVector& v, TArgs&&... args) { + auto a = std::array{std::forward(args)...}; + CHECK(std::ranges::equal(v, a, {}, &Constructable::getValue)); +} + +template +void assertEmpty(TVector& v) { + CHECK(0u == v.size()); + CHECK(v.empty()); + CHECK(v.begin() == v.end()); +} + +// Generate a sequence of values to initialize the vector. +template +void makeSequence(TVector& v, int start, int end) { + for (int i = start; i <= end; ++i) + v.push_back(Constructable(i)); +} + +template +constexpr static auto numBuiltinElts(const SmallVector&) { + return N; +} + +class SmallVectorTestBase { +public: + SmallVectorTestBase() { Constructable::reset(); } +}; + +template +class SmallVectorTest : public SmallVectorTestBase { +protected: + TVector theVector; + TVector otherVector; +}; + +using SmallVectorTestTypes = + std::tuple, SmallVector, + SmallVector, SmallVector>; + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorTest, "ConstructorIterTest", "[small_vec]", + SmallVectorTestTypes) { + int arr[] = {1, 2, 3}; + auto& v = this->theVector; + v = SmallVector(std::begin(arr), std::end(arr)); + assertValuesInOrder(v, 1, 2, 3); +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorTest, "ConstructorFromSpanSimpleTest", "[small_vec]", + SmallVectorTestTypes) { + std::array stdArray = {Constructable(1), Constructable(2), Constructable(3)}; + std::span array = stdArray; + + auto& v = this->theVector; + v = TestType(array); + assertValuesInOrder(v, 1, 2, 3); + REQUIRE(numBuiltinElts(TestType{}) == numBuiltinElts(v)); +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorTest, "EmptyVectorTest", "[small_vec]", + SmallVectorTestTypes) { + auto& v = this->theVector; + assertEmpty(v); + CHECK(v.rbegin() == v.rend()); + CHECK(0 == Constructable::getNumConstructorCalls()); + CHECK(0 == Constructable::getNumDestructorCalls()); +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorTest, "PushPopTest", "[small_vec]", + SmallVectorTestTypes) { + // Track whether the vector will potentially have to grow. + auto& v = this->theVector; + bool RequiresGrowth = v.capacity() < 3; + + // Push an element + v.push_back(Constructable(1)); + + assertValuesInOrder(v, 1); + CHECK_FALSE(v.begin() == v.end()); + CHECK_FALSE(v.empty()); + + v.push_back(Constructable(2)); + assertValuesInOrder(v, 1, 2); + + // Insert at beginning. Reserve space to avoid reference invalidation from v[1]. + v.reserve(v.size() + 1); + v.insert(v.begin(), v[1]); + assertValuesInOrder(v, 2, 1, 2); + + v.pop_back(); + assertValuesInOrder(v, 2, 1); + + // Pop remaining elements + v.resize(v.size() - 2); + assertEmpty(v); + + // Check number of constructor calls. Should be 2 for each list element, + // one for the argument to push_back, one for the argument to insert, + // and one for the list element itself. + if (!RequiresGrowth) { + CHECK(6 == Constructable::getNumConstructorCalls()); + CHECK(6 == Constructable::getNumDestructorCalls()); + } + else { + // If we had to grow the vector, these only have a lower bound, but should + // always be equal. + CHECK(6 <= Constructable::getNumConstructorCalls()); + CHECK(Constructable::getNumConstructorCalls() == Constructable::getNumDestructorCalls()); + } +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorTest, "ClearTest", "[small_vec]", SmallVectorTestTypes) { + auto& v = this->theVector; + v.reserve(2); + makeSequence(v, 1, 2); + v.clear(); + + assertEmpty(v); + CHECK(4 == Constructable::getNumConstructorCalls()); + CHECK(4 == Constructable::getNumDestructorCalls()); +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorTest, "ResizeShrinkTest", "[small_vec]", + SmallVectorTestTypes) { + auto& v = this->theVector; + v.reserve(3); + makeSequence(v, 1, 3); + v.resize(1); + + assertValuesInOrder(v, 1); + CHECK(6 == Constructable::getNumConstructorCalls()); + CHECK(5 == Constructable::getNumDestructorCalls()); +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorTest, "TruncateTest", "[small_vec]", + SmallVectorTestTypes) { + auto& v = this->theVector; + v.reserve(3); + makeSequence(v, 1, 3); + v.resize(1); + + assertValuesInOrder(v, 1); + CHECK(6 == Constructable::getNumConstructorCalls()); + CHECK(5 == Constructable::getNumDestructorCalls()); + + v.resize(1); + assertValuesInOrder(v, 1); + CHECK(6 == Constructable::getNumConstructorCalls()); + CHECK(5 == Constructable::getNumDestructorCalls()); + + v.resize(0); + assertEmpty(v); + CHECK(6 == Constructable::getNumConstructorCalls()); + CHECK(6 == Constructable::getNumDestructorCalls()); +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorTest, "ResizeGrowTest", "[small_vec]", + SmallVectorTestTypes) { + auto& v = this->theVector; + v.resize(2); + + CHECK(2 == Constructable::getNumConstructorCalls()); + CHECK(0 == Constructable::getNumDestructorCalls()); + CHECK(2u == v.size()); +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorTest, "ResizeWithElementsTest", "[small_vec]", + SmallVectorTestTypes) { + auto& v = this->theVector; + v.resize(2); + + Constructable::reset(); + + v.resize(4); + + int ctors = Constructable::getNumConstructorCalls(); + CHECK((ctors == 2 || ctors == 4)); + + int moveCtors = Constructable::getNumMoveConstructorCalls(); + CHECK((moveCtors == 0 || moveCtors == 2)); + + int dtors = Constructable::getNumDestructorCalls(); + CHECK((dtors == 0 || dtors == 2)); +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorTest, "ResizeFillTest", "[small_vec]", + SmallVectorTestTypes) { + auto& v = this->theVector; + v.resize(3, Constructable(77)); + assertValuesInOrder(v, 77, 77, 77); +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorTest, "OverflowTest", "[small_vec]", + SmallVectorTestTypes) { + // Push more elements than the fixed size. + auto& v = this->theVector; + makeSequence(v, 1, 10); + + // Test size and values. + CHECK(10u == v.size()); + for (int i = 0; i < 10; ++i) { + CHECK(i + 1 == v[i].getValue()); + } + + // Now resize back to fixed size. + v.resize(1); + assertValuesInOrder(v, 1); +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorTest, "IterationTest", "[small_vec]", + SmallVectorTestTypes) { + auto& v = this->theVector; + makeSequence(v, 1, 2); + + typename TestType::iterator it = v.begin(); + CHECK(*it == v.front()); + CHECK(*it == v[0]); + CHECK(1 == it->getValue()); + ++it; + CHECK(*it == v[1]); + CHECK(*it == v.back()); + CHECK(2 == it->getValue()); + ++it; + CHECK(it == v.end()); + --it; + CHECK(*it == v[1]); + CHECK(2 == it->getValue()); + --it; + CHECK(*it == v[0]); + CHECK(1 == it->getValue()); + + typename TestType::reverse_iterator rit = v.rbegin(); + CHECK(*rit == v[1]); + CHECK(2 == rit->getValue()); + ++rit; + CHECK(*rit == v[0]); + CHECK(1 == rit->getValue()); + ++rit; + CHECK(rit == v.rend()); + --rit; + CHECK(*rit == v[0]); + CHECK(1 == rit->getValue()); + --rit; + CHECK(*rit == v[1]); + CHECK(2 == rit->getValue()); +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorTest, "SwapTest", "[small_vec]", SmallVectorTestTypes) { + auto& v = this->theVector; + auto& u = this->otherVector; + makeSequence(v, 1, 2); + std::swap(v, u); + + assertEmpty(v); + assertValuesInOrder(u, 1, 2); +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorTest, "AppendTest", "[small_vec]", SmallVectorTestTypes) { + auto& v = this->theVector; + auto& u = this->otherVector; + makeSequence(u, 2, 3); + + v.push_back(Constructable(1)); + v.append(u.begin(), u.end()); + + assertValuesInOrder(v, 1, 2, 3); +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorTest, "AppendRepeatedTest", "[small_vec]", + SmallVectorTestTypes) { + auto& v = this->theVector; + v.push_back(Constructable(1)); + v.append(2, Constructable(77)); + assertValuesInOrder(v, 1, 77, 77); +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorTest, "AppendNonIterTest", "[small_vec]", + SmallVectorTestTypes) { + auto& v = this->theVector; + v.push_back(Constructable(1)); + v.append(2, 7); + assertValuesInOrder(v, 1, 7, 7); +} + +struct output_iterator { + typedef std::output_iterator_tag iterator_category; + typedef int value_type; + typedef int difference_type; + typedef value_type* pointer; + typedef value_type& reference; + operator int() { return 2; } + operator Constructable() { return 7; } +}; + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorTest, "AppendRepeatedNonForwardIterator", "[small_vec]", + SmallVectorTestTypes) { + auto& v = this->theVector; + v.push_back(Constructable(1)); + v.append(output_iterator(), output_iterator()); + assertValuesInOrder(v, 1, 7, 7); +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorTest, "AppendSmallVector", "[small_vec]", + SmallVectorTestTypes) { + SmallVector otherVector; + otherVector.assign(std::array{7, 7}); + + auto& v = this->theVector; + v.push_back(Constructable(1)); + v.append(otherVector); + assertValuesInOrder(v, 1, 7, 7); +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorTest, "AssignTest", "[small_vec]", SmallVectorTestTypes) { + auto& v = this->theVector; + v.push_back(Constructable(1)); + v.assign(2, Constructable(77)); + assertValuesInOrder(v, 77, 77); +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorTest, "AssignRangeTest", "[small_vec]", + SmallVectorTestTypes) { + auto& v = this->theVector; + v.push_back(Constructable(1)); + + int arr[] = {1, 2, 3}; + v.assign(std::begin(arr), std::end(arr)); + assertValuesInOrder(v, 1, 2, 3); +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorTest, "AssignNonIterTest", "[small_vec]", + SmallVectorTestTypes) { + auto& v = this->theVector; + v.push_back(Constructable(1)); + v.assign(2, 7); + assertValuesInOrder(v, 7, 7); +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorTest, "AssignSmallVector", "[small_vec]", + SmallVectorTestTypes) { + SmallVector otherVector; + otherVector.assign(std::array{7, 7}); + + auto& v = this->theVector; + v.push_back(Constructable(1)); + v.assign(otherVector); + assertValuesInOrder(v, 7, 7); +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorTest, "MoveAssignTest", "[small_vec]", + SmallVectorTestTypes) { + auto& v = this->theVector; + auto& u = this->otherVector; + + // Set up our vector with a single element, but enough capacity for 4. + v.reserve(4); + v.push_back(Constructable(1)); + + // Set up the other vector with 2 elements. + u.push_back(Constructable(2)); + u.push_back(Constructable(3)); + + // Move-assign from the other vector. + v = std::move(u); + + // Make sure we have the right result. + assertValuesInOrder(v, 2, 3); + + // Make sure the # of constructor/destructor calls line up. There + // are two live objects after clearing the other vector. + u.clear(); + CHECK(Constructable::getNumConstructorCalls() - 2 == Constructable::getNumDestructorCalls()); + + // There shouldn't be any live objects any more. + v.clear(); + CHECK(Constructable::getNumConstructorCalls() == Constructable::getNumDestructorCalls()); +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorTest, "EraseTest", "[small_vec]", SmallVectorTestTypes) { + auto& v = this->theVector; + makeSequence(v, 1, 3); + + const auto& theConstVector = v; + v.erase(theConstVector.begin()); + assertValuesInOrder(v, 2, 3); +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorTest, "EraseRangeTest", "[small_vec]", + SmallVectorTestTypes) { + auto& v = this->theVector; + makeSequence(v, 1, 3); + + const auto& theConstVector = v; + v.erase(theConstVector.begin(), theConstVector.begin() + 2); + assertValuesInOrder(v, 3); +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorTest, "InsertTest", "[small_vec]", SmallVectorTestTypes) { + auto& v = this->theVector; + makeSequence(v, 1, 3); + + typename TestType::iterator it = v.insert(v.begin() + 1, Constructable(77)); + CHECK(v.begin() + 1 == it); + assertValuesInOrder(v, 1, 77, 2, 3); +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorTest, "InsertCopy", "[small_vec]", SmallVectorTestTypes) { + auto& v = this->theVector; + makeSequence(v, 1, 3); + Constructable c(77); + + typename TestType::iterator it = v.insert(v.begin() + 1, c); + CHECK(v.begin() + 1 == it); + assertValuesInOrder(v, 1, 77, 2, 3); +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorTest, "InsertRepeatedTest", "[small_vec]", + SmallVectorTestTypes) { + auto& v = this->theVector; + makeSequence(v, 1, 4); + Constructable::reset(); + auto it = v.insert(v.begin() + 1, 2, Constructable(16)); + + CHECK((Constructable::getNumMoveConstructorCalls() == 2 || + Constructable::getNumMoveConstructorCalls() == 6)); + + // Move assign the next two to shift them up and make a gap. + CHECK(1 == Constructable::getNumMoveAssignmentCalls()); + + // Copy construct the two new elements from the parameter. + CHECK(2 == Constructable::getNumCopyAssignmentCalls()); + + // One copy for the temporary during insertion. + CHECK(1 == Constructable::getNumCopyConstructorCalls()); + CHECK(v.begin() + 1 == it); + assertValuesInOrder(v, 1, 16, 16, 2, 3, 4); +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorTest, "InsertRepeatedNonIterTest", "[small_vec]", + SmallVectorTestTypes) { + auto& v = this->theVector; + makeSequence(v, 1, 4); + Constructable::reset(); + auto it = v.insert(v.begin() + 1, 2, 7); + CHECK(v.begin() + 1 == it); + assertValuesInOrder(v, 1, 7, 7, 2, 3, 4); +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorTest, "InsertRepeatedAtEndTest", "[small_vec]", + SmallVectorTestTypes) { + auto& v = this->theVector; + makeSequence(v, 1, 4); + Constructable::reset(); + auto it = v.insert(v.end(), 2, Constructable(16)); + + // Just copy construct them into newly allocated space + CHECK(2 + 1 == Constructable::getNumCopyConstructorCalls()); + + // Move everything across if reallocation is needed. + CHECK((Constructable::getNumMoveConstructorCalls() == 0 || + Constructable::getNumMoveConstructorCalls() == 4)); + + // Without ever moving or copying anything else. + CHECK(0 == Constructable::getNumCopyAssignmentCalls()); + CHECK(0 == Constructable::getNumMoveAssignmentCalls()); + + CHECK(v.begin() + 4 == it); + assertValuesInOrder(v, 1, 2, 3, 4, 16, 16); +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorTest, "InsertRepeatedEmptyTest", "[small_vec]", + SmallVectorTestTypes) { + auto& v = this->theVector; + makeSequence(v, 10, 15); + + // Empty insert. + CHECK(v.end() == v.insert(v.end(), 0, Constructable(42))); + CHECK(v.begin() + 1 == v.insert(v.begin() + 1, 0, Constructable(42))); +} + +// Insert range. +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorTest, "InsertRangeTest", "[small_vec]", + SmallVectorTestTypes) { + auto& v = this->theVector; + Constructable arr[3] = {Constructable(77), Constructable(77), Constructable(77)}; + + makeSequence(v, 1, 3); + Constructable::reset(); + auto it = v.insert(v.begin() + 1, arr, arr + 3); + + // Move construct the top 3 elements into newly allocated space. + // Possibly move the whole sequence into new space first. + CHECK((Constructable::getNumMoveConstructorCalls() == 2 || + Constructable::getNumMoveConstructorCalls() == 5)); + + // Copy assign the lower 2 new elements into existing space. + CHECK(2 == Constructable::getNumCopyAssignmentCalls()); + + // Copy construct the third element into newly allocated space. + CHECK(1 == Constructable::getNumCopyConstructorCalls()); + CHECK(v.begin() + 1 == it); + assertValuesInOrder(v, 1, 77, 77, 77, 2, 3); +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorTest, "InsertRangeAtEndTest", "[small_vec]", + SmallVectorTestTypes) { + auto& v = this->theVector; + Constructable arr[3] = {Constructable(77), Constructable(77), Constructable(77)}; + + makeSequence(v, 1, 3); + + // Insert at end. + Constructable::reset(); + auto it = v.insert(v.end(), arr, arr + 3); + + // Copy construct the 3 elements into new space at the top. + CHECK(3 == Constructable::getNumCopyConstructorCalls()); + + // Don't copy/move anything else. + CHECK(0 == Constructable::getNumCopyAssignmentCalls()); + + // Reallocation might occur, causing all elements to be moved into the new + // buffer. + CHECK((Constructable::getNumMoveConstructorCalls() == 0 || + Constructable::getNumMoveConstructorCalls() == 3)); + CHECK(0 == Constructable::getNumMoveAssignmentCalls()); + CHECK(v.begin() + 3 == it); + assertValuesInOrder(v, 1, 2, 3, 77, 77, 77); +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorTest, "InsertEmptyRangeTest", "[small_vec]", + SmallVectorTestTypes) { + auto& v = this->theVector; + makeSequence(v, 1, 3); + + // Empty insert. + CHECK(v.end() == v.insert(v.end(), v.begin(), v.begin())); + CHECK(v.begin() + 1 == v.insert(v.begin() + 1, v.begin(), v.begin())); +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorTest, "ComparisonEqualityTest", "[small_vec]", + SmallVectorTestTypes) { + auto& v = this->theVector; + auto& u = this->otherVector; + makeSequence(v, 1, 3); + makeSequence(u, 1, 3); + + CHECK(v == u); + CHECK_FALSE(v != u); + + u.clear(); + makeSequence(u, 2, 4); + + CHECK_FALSE(v == u); + CHECK(v != u); +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorTest, "ComparisonLessThanTest", "[small_vec]", + SmallVectorTestTypes) { + auto& v = this->theVector; + auto& u = this->otherVector; + v.assign(std::array{1, 2, 4}); + u.assign(std::array{1, 4}); + + CHECK(v < u); + CHECK(v <= u); + CHECK_FALSE(v > u); + CHECK_FALSE(v >= u); + + CHECK_FALSE(u < v); + CHECK_FALSE(u <= v); + CHECK(u > v); + CHECK(u >= v); + + u.assign(std::array{1, 2, 4}); + + CHECK_FALSE(v < u); + CHECK(v <= u); + CHECK_FALSE(v > u); + CHECK(v >= u); + + CHECK_FALSE(u < v); + CHECK(u <= v); + CHECK_FALSE(u > v); + CHECK(u >= v); +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorTest, "ConstVectorTest", "[small_vec]", + SmallVectorTestTypes) { + const TestType constVector; + + CHECK(0u == constVector.size()); + CHECK(constVector.empty()); + CHECK(constVector.begin() == constVector.end()); +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorTest, "DirectVectorTest", "[small_vec]", + SmallVectorTestTypes) { + auto& v = this->theVector; + CHECK(0u == v.size()); + + v.reserve(4); + CHECK(4u <= v.capacity()); + CHECK(0 == Constructable::getNumConstructorCalls()); + + v.push_back(1); + v.push_back(2); + v.push_back(3); + v.push_back(4); + CHECK(4u == v.size()); + CHECK(8 == Constructable::getNumConstructorCalls()); + CHECK(1 == v[0].getValue()); + CHECK(2 == v[1].getValue()); + CHECK(3 == v[2].getValue()); + CHECK(4 == v[3].getValue()); +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorTest, "IteratorTest", "[small_vec]", + SmallVectorTestTypes) { + auto& v = this->theVector; + std::list l; + v.insert(v.end(), l.begin(), l.end()); +} + +template +class DualSmallVectorsTest; + +template +class DualSmallVectorsTest> : public SmallVectorTestBase { +protected: + VectorT1 theVector; + VectorT2 otherVector; +}; + +using DualSmallVectorTestTypes = std::tuple< + // Small mode -> Small mode. + std::pair, SmallVector>, + // Small mode -> Big mode. + std::pair, SmallVector>, + // Big mode -> Small mode. + std::pair, SmallVector>, + // Big mode -> Big mode. + std::pair, SmallVector>>; + +TEMPLATE_LIST_TEST_CASE_METHOD(DualSmallVectorsTest, "MoveAssignment", "[small_vec]", + DualSmallVectorTestTypes) { + auto& v = this->theVector; + auto& u = this->otherVector; + + // Set up our vector with four elements. + for (unsigned it = 0; it < 4; ++it) + u.push_back(Constructable(it)); + + const Constructable* origDataPtr = u.data(); + + // Move-assign from the other vector. + v = std::move(u); + + // Make sure we have the right result. + assertValuesInOrder(v, 0, 1, 2, 3); + + // Make sure the # of constructor/destructor calls line up. There + // are two live objects after clearing the other vector. + u.clear(); + CHECK(Constructable::getNumConstructorCalls() - 4 == Constructable::getNumDestructorCalls()); + + // If the source vector (otherVector) was in small-mode, assert that we just + // moved the data pointer over. + CHECK((numBuiltinElts(u) == 4 || v.data() == origDataPtr)); + + // There shouldn't be any live objects any more. + v.clear(); + CHECK(Constructable::getNumConstructorCalls() == Constructable::getNumDestructorCalls()); + + // We shouldn't have copied anything in this whole process. + CHECK(Constructable::getNumCopyConstructorCalls() == 0); +} + +struct NotAssignable { + int& x; + NotAssignable(int& x) : x(x) {} +}; + +TEST_CASE("SmallVector::NoAssignTest", "[small_vec]") { + int x = 0; + SmallVector vec; + vec.push_back(NotAssignable(x)); + + x = 42; + CHECK(42 == vec.back().x); +} + +struct MovedFrom { + bool hasValue; + MovedFrom() : hasValue(true) {} + MovedFrom(MovedFrom&& m) : hasValue(m.hasValue) { m.hasValue = false; } + MovedFrom& operator=(MovedFrom&& m) { + hasValue = m.hasValue; + m.hasValue = false; + return *this; + } +}; + +TEST_CASE("SmallVector::MidInsert", "[small_vec]") { + SmallVector v; + v.push_back(MovedFrom()); + v.insert(v.begin(), MovedFrom()); + for (MovedFrom& m : v) + CHECK(m.hasValue); +} + +enum class EmplaceableArgState { Defaulted, Arg, LValue, RValue, Failure }; +template +struct EmplaceableArg { + EmplaceableArgState state; + + EmplaceableArg() : state(EmplaceableArgState::Defaulted) {} + EmplaceableArg(EmplaceableArg&& x) : + state(x.state == EmplaceableArgState::Arg ? EmplaceableArgState::RValue + : EmplaceableArgState::Failure) {} + EmplaceableArg(EmplaceableArg& x) : + state(x.state == EmplaceableArgState::Arg ? EmplaceableArgState::LValue + : EmplaceableArgState::Failure) {} + + explicit EmplaceableArg(bool) : state(EmplaceableArgState::Arg) {} + +private: + EmplaceableArg& operator=(EmplaceableArg&&) = delete; + EmplaceableArg& operator=(const EmplaceableArg&) = delete; +}; + +enum class EmplaceableState { Emplaced, Moved }; +struct Emplaceable { + EmplaceableArg<0> a0; + EmplaceableArg<1> a1; + EmplaceableArg<2> a2; + EmplaceableArg<3> a3; + EmplaceableState state; + + Emplaceable() : state(EmplaceableState::Emplaced) {} + + template + explicit Emplaceable(A0Ty&& a0) : + a0(std::forward(a0)), state(EmplaceableState::Emplaced) {} + + template + Emplaceable(A0Ty&& a0, A1Ty&& a1) : + a0(std::forward(a0)), a1(std::forward(a1)), state(EmplaceableState::Emplaced) {} + + template + Emplaceable(A0Ty&& a0, A1Ty&& a1, A2Ty&& a2) : + a0(std::forward(a0)), a1(std::forward(a1)), a2(std::forward(a2)), + state(EmplaceableState::Emplaced) {} + + template + Emplaceable(A0Ty&& a0, A1Ty&& a1, A2Ty&& a2, A3Ty&& a3) : + a0(std::forward(a0)), a1(std::forward(a1)), a2(std::forward(a2)), + a3(std::forward(a3)), state(EmplaceableState::Emplaced) {} + + Emplaceable(Emplaceable&&) : state(EmplaceableState::Moved) {} + Emplaceable& operator=(Emplaceable&&) { + state = EmplaceableState::Moved; + return *this; + } + +private: + Emplaceable(const Emplaceable&) = delete; + Emplaceable& operator=(const Emplaceable&) = delete; +}; + +TEST_CASE("SmallVector::EmplaceBack", "[small_vec]") { + EmplaceableArg<0> a0(true); + EmplaceableArg<1> a1(true); + EmplaceableArg<2> a2(true); + EmplaceableArg<3> a3(true); + { + SmallVector v; + Emplaceable& back = v.emplace_back(); + CHECK(&back == &v.back()); + CHECK(v.size() == 1); + CHECK(back.state == EmplaceableState::Emplaced); + CHECK(back.a0.state == EmplaceableArgState::Defaulted); + CHECK(back.a1.state == EmplaceableArgState::Defaulted); + CHECK(back.a2.state == EmplaceableArgState::Defaulted); + CHECK(back.a3.state == EmplaceableArgState::Defaulted); + } + { + SmallVector v; + Emplaceable& back = v.emplace_back(std::move(a0)); + CHECK(&back == &v.back()); + CHECK(v.size() == 1); + CHECK(back.state == EmplaceableState::Emplaced); + CHECK(back.a0.state == EmplaceableArgState::RValue); + CHECK(back.a1.state == EmplaceableArgState::Defaulted); + CHECK(back.a2.state == EmplaceableArgState::Defaulted); + CHECK(back.a3.state == EmplaceableArgState::Defaulted); + } + { + SmallVector v; + Emplaceable& back = v.emplace_back(a0); + CHECK(&back == &v.back()); + CHECK(v.size() == 1); + CHECK(back.state == EmplaceableState::Emplaced); + CHECK(back.a0.state == EmplaceableArgState::LValue); + CHECK(back.a1.state == EmplaceableArgState::Defaulted); + CHECK(back.a2.state == EmplaceableArgState::Defaulted); + CHECK(back.a3.state == EmplaceableArgState::Defaulted); + } + { + SmallVector v; + Emplaceable& back = v.emplace_back(a0, a1); + CHECK(&back == &v.back()); + CHECK(v.size() == 1); + CHECK(back.state == EmplaceableState::Emplaced); + CHECK(back.a0.state == EmplaceableArgState::LValue); + CHECK(back.a1.state == EmplaceableArgState::LValue); + CHECK(back.a2.state == EmplaceableArgState::Defaulted); + CHECK(back.a3.state == EmplaceableArgState::Defaulted); + } + { + SmallVector v; + Emplaceable& back = v.emplace_back(std::move(a0), std::move(a1)); + CHECK(&back == &v.back()); + CHECK(v.size() == 1); + CHECK(back.state == EmplaceableState::Emplaced); + CHECK(back.a0.state == EmplaceableArgState::RValue); + CHECK(back.a1.state == EmplaceableArgState::RValue); + CHECK(back.a2.state == EmplaceableArgState::Defaulted); + CHECK(back.a3.state == EmplaceableArgState::Defaulted); + } + { + SmallVector v; + Emplaceable& back = v.emplace_back(std::move(a0), a1, std::move(a2), a3); + CHECK(&back == &v.back()); + CHECK(v.size() == 1); + CHECK(back.state == EmplaceableState::Emplaced); + CHECK(back.a0.state == EmplaceableArgState::RValue); + CHECK(back.a1.state == EmplaceableArgState::LValue); + CHECK(back.a2.state == EmplaceableArgState::RValue); + CHECK(back.a3.state == EmplaceableArgState::LValue); + } + { + SmallVector v; + v.emplace_back(); + v.emplace_back(42); + CHECK(2U == v.size()); + CHECK(0 == v[0]); + CHECK(42 == v[1]); + } +} + +TEST_CASE("SmallVector::DefaultInlinedElements", "[small_vec]") { + SmallVector v; + CHECK(v.empty()); + v.push_back(7); + CHECK(v[0] == 7); + + // Check that at least a couple layers of nested SmallVector's are allowed + // by the default inline elements policy. + SmallVector>> nestedV; + nestedV.emplace_back().emplace_back().emplace_back(42); + CHECK(nestedV[0][0][0] == 42); +} + +struct To { + int content; + bool operator==(const To& other) const = default; +}; + +class From { +public: + From() = default; + From(To m) { t = m; } + operator To() const { return t; } + +private: + To t; +}; + +TEST_CASE("SmallVector::ConstructFromArrayRefOfConvertibleType", "[small_vec]") { + To to1{1}, to2{2}, to3{3}; + std::vector stdVector = {From(to1), From(to2), From(to3)}; + std::span array = stdVector; + { + SmallVector vector(array); + + REQUIRE(array.size() == vector.size()); + for (size_t it = 0; it < array.size(); ++it) + CHECK(array[it] == vector[it]); + } + { + SmallVector vector(stdVector); + + REQUIRE(stdVector.size() == vector.size()); + REQUIRE(4u == numBuiltinElts(vector)); + for (size_t it = 0; it < stdVector.size(); ++it) + CHECK(stdVector[it] == vector[it]); + } +} + +template +class SmallVectorReferenceInvalidationTest : public SmallVectorTestBase { +protected: + TVector v; + + template + static bool isValueType() { + return std::is_same_v; + } + + SmallVectorReferenceInvalidationTest() { + // Fill up the small size so that insertions move the elements. + for (int it = 0, e = numBuiltinElts(v); it != e; ++it) + v.emplace_back(it + 1); + } +}; + +// 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 = + std::tuple, SmallVector>; + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorReferenceInvalidationTest, "PushBack", "[small_vec]", + SmallVectorReferenceInvalidationTestTypes) { + // Note: setup adds [1, 2, ...] to v until it's at capacity in small mode. + auto& v = this->v; + int n = numBuiltinElts(v); + + // Push back a reference to last element when growing from small storage. + v.push_back(v.back()); + CHECK(n == v.back()); + + // Check that the old value is still there (not moved away). + CHECK(n == v[v.size() - 2]); + + // Fill storage again. + v.back() = v.size(); + while (v.size() < v.capacity()) + v.push_back(v.size() + 1); + + // Push back a reference to last element when growing from large storage. + v.push_back(v.back()); + CHECK(int(v.size()) - 1 == v.back()); +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorReferenceInvalidationTest, "PushBackMoved", "[small_vec]", + SmallVectorReferenceInvalidationTestTypes) { + // Note: setup adds [1, 2, ...] to v until it's at capacity in small mode. + auto& v = this->v; + int n = numBuiltinElts(v); + + // Push back a reference to last element when growing from small storage. + v.push_back(std::move(v.back())); + CHECK(n == v.back()); + if (this->template isValueType()) { + // Check that the value was moved (not copied). + CHECK(0 == v[v.size() - 2]); + } + + // Fill storage again. + v.back() = v.size(); + while (v.size() < v.capacity()) + v.push_back(v.size() + 1); + + // Push back a reference to last element when growing from large storage. + v.push_back(std::move(v.back())); + + // Check the values. + CHECK(int(v.size()) - 1 == v.back()); + if (this->template isValueType()) { + // Check the value got moved out. + CHECK(0 == v[v.size() - 2]); + } +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorReferenceInvalidationTest, "Resize", "[small_vec]", + SmallVectorReferenceInvalidationTestTypes) { + auto& v = this->v; + int n = numBuiltinElts(v); + v.resize(n + 1, v.back()); + CHECK(n == v.back()); + + // Resize to add enough elements that v will grow again. + v.resize(v.capacity() + 1, v.front()); + CHECK(1 == v.back()); +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorReferenceInvalidationTest, "Append", "[small_vec]", + SmallVectorReferenceInvalidationTestTypes) { + auto& v = this->v; + v.append(1, v.back()); + int n = numBuiltinElts(v); + CHECK(n == v[n - 1]); + + // Append enough more elements that v will grow again. This tests growing + // when already in large mode. + v.append(v.capacity() - v.size() + 1, v.front()); + CHECK(1 == v.back()); +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorReferenceInvalidationTest, "Assign", "[small_vec]", + SmallVectorReferenceInvalidationTestTypes) { + // Note: setup adds [1, 2, ...] to v until it's at capacity in small mode. + auto& v = this->v; + int n = numBuiltinElts(v); + REQUIRE(unsigned(n) == v.size()); + REQUIRE(unsigned(n) == v.capacity()); + + // Check assign that shrinks in small mode. + v.assign(1, v.back()); + CHECK(1u == v.size()); + CHECK(n == v[0]); + + // Check assign that grows within small mode. + REQUIRE(v.size() <= v.capacity()); + v.assign(v.capacity(), v.back()); + for (int it = 0, e = v.size(); it != e; ++it) { + CHECK(n == v[it]); + + // Reset to [1, 2, ...]. + v[it] = it + 1; + } + + // Check assign that grows to large mode. + REQUIRE(2 == v[1]); + v.assign(v.capacity() + 1, v[1]); + for (int it = 0, e = v.size(); it != e; ++it) { + CHECK(2 == v[it]); + + // Reset to [1, 2, ...]. + v[it] = it + 1; + } + + // Check assign that shrinks in large mode. + v.assign(1, v[1]); + CHECK(2 == v[0]); +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorReferenceInvalidationTest, "AssignRange", "[small_vec]", + SmallVectorReferenceInvalidationTestTypes) { + auto& v = this->v; + v.assign(v.begin(), v.begin()); + CHECK(v.empty()); +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorReferenceInvalidationTest, "Insert", "[small_vec]", + SmallVectorReferenceInvalidationTestTypes) { + // Note: setup adds [1, 2, ...] to v until it's at capacity in small mode. + auto& v = this->v; + + // Insert a reference to the back (not at end() or else insert delegates to + // push_back()), growing out of small mode. Confirm the value was copied out + // (moving out Constructable sets it to 0). + v.insert(v.begin(), v.back()); + CHECK(int(v.size() - 1) == v.front()); + CHECK(int(v.size() - 1) == v.back()); + + // Fill up the vector again. + while (v.size() < v.capacity()) + v.push_back(v.size() + 1); + + // Grow again from large storage to large storage. + v.insert(v.begin(), v.back()); + CHECK(int(v.size() - 1) == v.front()); + CHECK(int(v.size() - 1) == v.back()); +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorReferenceInvalidationTest, "InsertMoved", "[small_vec]", + SmallVectorReferenceInvalidationTestTypes) { + // Note: setup adds [1, 2, ...] to v until it's at capacity in small mode. + auto& v = this->v; + + // Insert a reference to the back (not at end() or else insert delegates to + // push_back()), growing out of small mode. Confirm the value was copied out + // (moving out Constructable sets it to 0). + v.insert(v.begin(), std::move(v.back())); + CHECK(int(v.size() - 1) == v.front()); + if (this->template isValueType()) { + // Check the value got moved out. + CHECK(0 == v.back()); + } + + // Fill up the vector again. + while (v.size() < v.capacity()) + v.push_back(v.size() + 1); + + // Grow again from large storage to large storage. + v.insert(v.begin(), std::move(v.back())); + CHECK(int(v.size() - 1) == v.front()); + if (this->template isValueType()) { + // Check the value got moved out. + CHECK(0 == v.back()); + } +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorReferenceInvalidationTest, "InsertN", "[small_vec]", + SmallVectorReferenceInvalidationTestTypes) { + // Cover NumToInsert <= this->end() - it. + auto& v = this->v; + v.insert(v.begin() + 1, 1, v.back()); + int n = numBuiltinElts(v); + CHECK(n == v[1]); + + // Cover NumToInsert > this->end() - it, inserting enough elements that v will + // also grow again; v.capacity() will be more elements than necessary but + // it's a simple way to cover both conditions. + v.insert(v.begin(), v.capacity(), v.front()); + CHECK(1 == v.front()); +} + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorReferenceInvalidationTest, "EmplaceBack", "[small_vec]", + SmallVectorReferenceInvalidationTestTypes) { + // Note: setup adds [1, 2, ...] to v until it's at capacity in small mode. + auto& v = this->v; + int n = numBuiltinElts(v); + + // Push back a reference to last element when growing from small storage. + v.emplace_back(v.back()); + CHECK(n == v.back()); + + // Check that the old value is still there (not moved away). + CHECK(n == v[v.size() - 2]); + + // Fill storage again. + v.back() = v.size(); + while (v.size() < v.capacity()) + v.push_back(v.size() + 1); + + // Push back a reference to last element when growing from large storage. + v.emplace_back(v.back()); + CHECK(int(v.size()) - 1 == v.back()); +} + +template +class SmallVectorInternalReferenceInvalidationTest : public SmallVectorTestBase { +protected: + TVector v; + + SmallVectorInternalReferenceInvalidationTest() { + // Fill up the small size so that insertions move the elements. + for (int it = 0, e = numBuiltinElts(v); it != e; ++it) + v.emplace_back(it + 1, it + 1); + } +}; + +using SmallVectorInternalReferenceInvalidationTestTypes = + std::tuple, 3>, + SmallVector, 3>>; + +TEMPLATE_LIST_TEST_CASE_METHOD(SmallVectorInternalReferenceInvalidationTest, "EmplaceBack", + "[small_vec]", SmallVectorInternalReferenceInvalidationTestTypes) { + // Note: setup adds [1, 2, ...] to v until it's at capacity in small mode. + auto& v = this->v; + int n = numBuiltinElts(v); + + // Push back a reference to last element when growing from small storage. + v.emplace_back(v.back().first, v.back().second); + CHECK(n == v.back().first); + CHECK(n == v.back().second); + + // Check that the old value is still there (not moved away). + CHECK(n == v[v.size() - 2].first); + CHECK(n == v[v.size() - 2].second); + + // Fill storage again. + v.back().first = v.back().second = v.size(); + while (v.size() < v.capacity()) + v.emplace_back((int)v.size() + 1, (int)v.size() + 1); + + // Push back a reference to last element when growing from large storage. + v.emplace_back(v.back().first, v.back().second); + CHECK(int(v.size()) - 1 == v.back().first); + CHECK(int(v.size()) - 1 == v.back().second); +} + +} // end namespace From 4906dfae06985bec5c02fc46c175b372093c925e Mon Sep 17 00:00:00 2001 From: MikePopoloski Date: Sun, 25 Jun 2023 10:31:48 -0400 Subject: [PATCH 2/4] Remove unneeded SLANG_EXPORT directives --- include/slang/util/Enum.h | 2 +- include/slang/util/Util.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/slang/util/Enum.h b/include/slang/util/Enum.h index 01e18660b..a6a45cc53 100644 --- a/include/slang/util/Enum.h +++ b/include/slang/util/Enum.h @@ -121,7 +121,7 @@ inline constexpr bitmask_detail::underlying_type_t get_enum_mask(const T&) no /// of bitwise-combined flags. Built-in strongly-typed C++ enums are not otherwise /// combinable via operators like | and &. template -class SLANG_EXPORT bitmask { +class bitmask { public: using underlying_type = bitmask_detail::underlying_type_t; diff --git a/include/slang/util/Util.h b/include/slang/util/Util.h index be6dd9a20..bace31f5e 100644 --- a/include/slang/util/Util.h +++ b/include/slang/util/Util.h @@ -157,7 +157,7 @@ class SLANG_EXPORT AssertionException : public std::logic_error { /// The real value of this type is in documenting in the API the intentions of the pointer, /// so that consumers don't need to add explicit null checks. template -class SLANG_EXPORT not_null { +class not_null { public: static_assert(std::is_assignable::value, "T cannot be assigned nullptr."); From e7d434d3ded712b804bb767a471f19bf7b208b6f Mon Sep 17 00:00:00 2001 From: MikePopoloski Date: Sun, 25 Jun 2023 10:53:33 -0400 Subject: [PATCH 3/4] Disable mimalloc for python binding builds --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 79954f61a..eead3d5b8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -143,8 +143,8 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") add_compile_options(/utf-8 /bigobj /permissive-) endif() -# mimalloc is incompatible with Python bindings on MacOS -if(CMAKE_SYSTEM_NAME MATCHES "Darwin" AND SLANG_INCLUDE_PYLIB) +# mimalloc is incompatible with Python bindings +if(SLANG_INCLUDE_PYLIB) set(SLANG_USE_MIMALLOC OFF) endif() From f94723c66e06533e0ba6707e464e1ddf12d97c24 Mon Sep 17 00:00:00 2001 From: MikePopoloski Date: Sun, 25 Jun 2023 10:55:57 -0400 Subject: [PATCH 4/4] Update docs --- docs/building.dox | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/docs/building.dox b/docs/building.dox index 04eefb60b..5b7cb3cfb 100644 --- a/docs/building.dox +++ b/docs/building.dox @@ -16,14 +16,11 @@ git clone https://github.com/MikePopoloski/slang.git slang requires the following tools in order to build: - [python 3](https://www.python.org/) - [CMake](https://cmake.org/) (3.15 or later) -- C++20 compatible compiler - - GCC 10 is the minimum supported version - - clang 10 is the minimum supported version +- C++20 compatible compiler. Minimum supported compiler versions: + - GCC 10 + - clang 16 + - XCode 14.3 - MSVC support is tested only against the most recent update of VS 2022 - - Other compilers (including earlier versions of the ones listed above) may or may not work. - Note that AppleClang has its own versioning scheme -- you generally need a newer AppleClang - to get full C++20 support, and Apple ties Xcode updates to OS versions so you may be forced - to update your OS. @section building-start Quick Start