mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
251 lines
7.8 KiB
C++
251 lines
7.8 KiB
C++
#include "swift/Basic/PointerIntEnum.h"
|
|
#include "swift/Basic/type_traits.h"
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
using namespace swift;
|
|
|
|
namespace {
|
|
|
|
enum class EnumTy : unsigned {
|
|
Ptr1 = 0,
|
|
Ptr2 = 1,
|
|
Ptr3 = 2,
|
|
FirstPointerKind = Ptr1,
|
|
LastPointerKind = Ptr3,
|
|
|
|
// Index Projection Kinds
|
|
FirstIndexKind = 7,
|
|
Index1 = PointerIntEnumIndexKindValue<0, EnumTy>::value,
|
|
Index2 = PointerIntEnumIndexKindValue<1, EnumTy>::value,
|
|
Index3 = PointerIntEnumIndexKindValue<2, EnumTy>::value,
|
|
Index4 = PointerIntEnumIndexKindValue<3, EnumTy>::value,
|
|
Index5 = PointerIntEnumIndexKindValue<4, EnumTy>::value,
|
|
LastIndexKind = Index5,
|
|
};
|
|
|
|
using PointerIntEnumTy =
|
|
PointerIntEnum<EnumTy, void *, 3, 4, llvm::PointerLikeTypeTraits<void *>>;
|
|
|
|
static_assert(IsTriviallyCopyable<PointerIntEnumTy>::value,
|
|
"PointerIntEnum type should be trivially copyable");
|
|
|
|
static constexpr uintptr_t InvalidStorage = uintptr_t(0) - 1;
|
|
|
|
} // end anonymous namespace
|
|
|
|
TEST(PointerIntEnumTest, DefaultConstructorYieldsInvalid) {
|
|
PointerIntEnumTy Enum;
|
|
EXPECT_FALSE(Enum.isValid());
|
|
EXPECT_TRUE(Enum.getStorage() == InvalidStorage);
|
|
}
|
|
|
|
TEST(PointerIntEnumTest, PointerConstructor) {
|
|
int *data = new int[1];
|
|
PointerIntEnumTy Enum(EnumTy::Ptr1, data);
|
|
EXPECT_TRUE(Enum.isValid());
|
|
EXPECT_EQ(*Enum.getKind(), EnumTy::Ptr1);
|
|
EXPECT_EQ(Enum.getPointer(), data);
|
|
|
|
// Make sure that the value is laid out correctly in memory.
|
|
uintptr_t Value = uintptr_t(data) | uintptr_t(EnumTy::Ptr1);
|
|
EXPECT_EQ(Enum.getStorage(), Value);
|
|
|
|
delete[] data;
|
|
}
|
|
|
|
TEST(PointerIntEnumTest, IndexConstructor) {
|
|
// First test a case that we can represent.
|
|
{
|
|
PointerIntEnumTy Enum(EnumTy::Index3, 0xBEEF);
|
|
EXPECT_TRUE(Enum.isValid());
|
|
EXPECT_EQ(*Enum.getKind(), EnumTy::Index3);
|
|
EXPECT_EQ(Enum.getIndex(), uintptr_t(0xBEEF));
|
|
|
|
// Make sure that the value is laid out correctly in memory.
|
|
uintptr_t Value = (uintptr_t(0xBEEF) << 7) | uintptr_t(EnumTy::Index3);
|
|
EXPECT_EQ(Enum.getStorage(), Value);
|
|
}
|
|
|
|
// Then test the boundary from representable index to unrepresentable index.
|
|
uintptr_t MaxIndex = (uintptr_t(1) << (sizeof(uintptr_t) * CHAR_BIT - 7)) - 2;
|
|
{
|
|
PointerIntEnumTy Enum(EnumTy::Index3, MaxIndex + 1);
|
|
EXPECT_FALSE(Enum.isValid());
|
|
EXPECT_FALSE(Enum.getKind());
|
|
EXPECT_EQ(Enum.getStorage(), InvalidStorage);
|
|
}
|
|
|
|
{
|
|
PointerIntEnumTy Enum(EnumTy::Index4, MaxIndex);
|
|
EXPECT_TRUE(Enum.isValid());
|
|
EXPECT_EQ(*Enum.getKind(), EnumTy::Index4);
|
|
EXPECT_EQ(Enum.getIndex(), MaxIndex);
|
|
|
|
// Make sure that the value is laid out correctly in memory.
|
|
uintptr_t Value = (uintptr_t(MaxIndex) << 7) | uintptr_t(EnumTy::Index4);
|
|
EXPECT_EQ(Enum.getStorage(), Value);
|
|
}
|
|
}
|
|
|
|
TEST(PointerIntEnumTest, CopyConstructorAssignment) {
|
|
PointerIntEnumTy IntEnum(EnumTy::Index3, 0xBEEF);
|
|
uintptr_t IntEnumStorageValue =
|
|
(uintptr_t(0xBEEF) << 7) | uintptr_t(EnumTy::Index3);
|
|
int *data = new int[1];
|
|
PointerIntEnumTy PtrEnum(EnumTy::Ptr2, data);
|
|
uintptr_t PtrEnumStorageValue = uintptr_t(data) | uintptr_t(EnumTy::Ptr2);
|
|
|
|
PointerIntEnumTy Enum2(IntEnum);
|
|
PointerIntEnumTy Enum3 = IntEnum;
|
|
|
|
EXPECT_TRUE(Enum2.isValid());
|
|
EXPECT_EQ(*Enum2.getKind(), EnumTy::Index3);
|
|
EXPECT_EQ(Enum2.getIndex(), uintptr_t(0xBEEF));
|
|
EXPECT_EQ(Enum2.getStorage(), IntEnumStorageValue);
|
|
EXPECT_EQ(IntEnum, Enum2);
|
|
EXPECT_NE(PtrEnum, Enum2);
|
|
|
|
EXPECT_TRUE(Enum3.isValid());
|
|
EXPECT_EQ(*Enum3.getKind(), EnumTy::Index3);
|
|
EXPECT_EQ(Enum3.getIndex(), uintptr_t(0xBEEF));
|
|
EXPECT_EQ(Enum3.getStorage(), IntEnumStorageValue);
|
|
EXPECT_EQ(IntEnum, Enum3);
|
|
EXPECT_NE(PtrEnum, Enum3);
|
|
|
|
Enum3 = PtrEnum;
|
|
PointerIntEnumTy Enum4(PtrEnum);
|
|
|
|
EXPECT_TRUE(Enum3.isValid());
|
|
EXPECT_EQ(*Enum3.getKind(), EnumTy::Ptr2);
|
|
EXPECT_EQ(Enum3.getPointer(), data);
|
|
EXPECT_EQ(Enum3.getStorage(), PtrEnumStorageValue);
|
|
EXPECT_EQ(Enum3, PtrEnum);
|
|
EXPECT_NE(Enum3, IntEnum);
|
|
|
|
EXPECT_TRUE(Enum4.isValid());
|
|
EXPECT_EQ(*Enum4.getKind(), EnumTy::Ptr2);
|
|
EXPECT_EQ(Enum4.getPointer(), data);
|
|
EXPECT_EQ(Enum4.getStorage(), PtrEnumStorageValue);
|
|
EXPECT_EQ(Enum4, PtrEnum);
|
|
EXPECT_NE(Enum4, IntEnum);
|
|
|
|
// Round trip Enum3
|
|
Enum3 = IntEnum;
|
|
EXPECT_TRUE(Enum3.isValid());
|
|
EXPECT_EQ(*Enum3.getKind(), EnumTy::Index3);
|
|
EXPECT_EQ(Enum3.getIndex(), uintptr_t(0xBEEF));
|
|
EXPECT_EQ(Enum3.getStorage(), IntEnumStorageValue);
|
|
EXPECT_EQ(IntEnum, Enum3);
|
|
EXPECT_NE(PtrEnum, Enum3);
|
|
|
|
delete[] data;
|
|
}
|
|
|
|
// We have a trivial move constructor, so we copy when we move.
|
|
TEST(PointerIntEnumTest, MoveConstructorAssignment) {
|
|
PointerIntEnumTy IntEnum(EnumTy::Index3, 0xBEEF);
|
|
uintptr_t IntEnumStorageValue =
|
|
(uintptr_t(0xBEEF) << 7) | uintptr_t(EnumTy::Index3);
|
|
int *data = new int[1];
|
|
PointerIntEnumTy PtrEnum(EnumTy::Ptr2, data);
|
|
uintptr_t PtrEnumStorageValue = uintptr_t(data) | uintptr_t(EnumTy::Ptr2);
|
|
|
|
PointerIntEnumTy Enum2(std::move(IntEnum));
|
|
PointerIntEnumTy Enum3 = std::move(IntEnum);
|
|
|
|
EXPECT_TRUE(Enum2.isValid());
|
|
EXPECT_EQ(*Enum2.getKind(), EnumTy::Index3);
|
|
EXPECT_EQ(Enum2.getIndex(), uintptr_t(0xBEEF));
|
|
EXPECT_EQ(Enum2.getStorage(), IntEnumStorageValue);
|
|
EXPECT_EQ(IntEnum, Enum2);
|
|
EXPECT_NE(PtrEnum, Enum2);
|
|
|
|
EXPECT_TRUE(Enum3.isValid());
|
|
EXPECT_EQ(*Enum3.getKind(), EnumTy::Index3);
|
|
EXPECT_EQ(Enum3.getIndex(), uintptr_t(0xBEEF));
|
|
EXPECT_EQ(Enum3.getStorage(), IntEnumStorageValue);
|
|
EXPECT_EQ(IntEnum, Enum3);
|
|
EXPECT_NE(PtrEnum, Enum3);
|
|
|
|
Enum3 = std::move(PtrEnum);
|
|
PointerIntEnumTy Enum4(std::move(PtrEnum));
|
|
|
|
EXPECT_TRUE(Enum3.isValid());
|
|
EXPECT_EQ(*Enum3.getKind(), EnumTy::Ptr2);
|
|
EXPECT_EQ(Enum3.getPointer(), data);
|
|
EXPECT_EQ(Enum3.getStorage(), PtrEnumStorageValue);
|
|
EXPECT_EQ(Enum3, PtrEnum);
|
|
EXPECT_NE(Enum3, IntEnum);
|
|
|
|
EXPECT_TRUE(Enum4.isValid());
|
|
EXPECT_EQ(*Enum4.getKind(), EnumTy::Ptr2);
|
|
EXPECT_EQ(Enum4.getPointer(), data);
|
|
EXPECT_EQ(Enum4.getStorage(), PtrEnumStorageValue);
|
|
EXPECT_EQ(Enum4, PtrEnum);
|
|
EXPECT_NE(Enum4, IntEnum);
|
|
|
|
// Round trip Enum3
|
|
Enum3 = std::move(IntEnum);
|
|
EXPECT_TRUE(Enum3.isValid());
|
|
EXPECT_EQ(*Enum3.getKind(), EnumTy::Index3);
|
|
EXPECT_EQ(Enum3.getIndex(), uintptr_t(0xBEEF));
|
|
EXPECT_EQ(Enum3.getStorage(), IntEnumStorageValue);
|
|
EXPECT_EQ(IntEnum, Enum3);
|
|
EXPECT_NE(PtrEnum, Enum3);
|
|
|
|
delete[] data;
|
|
}
|
|
|
|
TEST(PointerIntEnumTest, Comparisons) {
|
|
PointerIntEnumTy IndexCase1(EnumTy::Index1, 5);
|
|
|
|
// Make sure that enums with different cases but the same value always compare
|
|
// different.
|
|
PointerIntEnumTy IndexCase2(EnumTy::Index2, 5);
|
|
EXPECT_NE(IndexCase1, IndexCase2);
|
|
|
|
// Make sure that enums with the same case and the same value compare equal.
|
|
PointerIntEnumTy IndexCase3(EnumTy::Index1, 5);
|
|
EXPECT_EQ(IndexCase1, IndexCase3);
|
|
|
|
// Make sure that enums with the same case, but different values do not
|
|
// compare equal.
|
|
PointerIntEnumTy IndexCase4(EnumTy::Index1, 6);
|
|
EXPECT_NE(IndexCase1, IndexCase4);
|
|
|
|
int *data1 = new int[1];
|
|
int *data2 = new int[1];
|
|
PointerIntEnumTy PtrCase1(EnumTy::Ptr1, data1);
|
|
|
|
// Test that pointer enums with different cases but the same value compare
|
|
// different.
|
|
PointerIntEnumTy PtrCase2(EnumTy::Ptr2, data1);
|
|
EXPECT_NE(PtrCase1, PtrCase2);
|
|
|
|
// Test that pointer enums with the same case and data are equal.
|
|
PointerIntEnumTy PtrCase3(EnumTy::Ptr1, data1);
|
|
EXPECT_EQ(PtrCase1, PtrCase3);
|
|
|
|
// Test that pointer enums with the same case but different data are not
|
|
// equal.
|
|
PointerIntEnumTy PtrCase4(EnumTy::Ptr1, data2);
|
|
EXPECT_NE(PtrCase1, PtrCase4);
|
|
|
|
// Test that pointers and indices compare differently.
|
|
EXPECT_NE(IndexCase1, PtrCase1);
|
|
|
|
// Test comparison in between invalid and valid PointerIntEnums.
|
|
PointerIntEnumTy Invalid1;
|
|
PointerIntEnumTy Invalid2;
|
|
EXPECT_EQ(Invalid1, Invalid2);
|
|
EXPECT_NE(Invalid1, IndexCase1);
|
|
EXPECT_NE(Invalid1, PtrCase1);
|
|
EXPECT_NE(IndexCase1, Invalid1);
|
|
EXPECT_NE(PtrCase1, Invalid1);
|
|
|
|
|
|
delete[] data2;
|
|
delete[] data1;
|
|
}
|