#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>; static_assert(IsTriviallyCopyable::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; }