diff --git a/include/CppInterOp/CppInterOp.h b/include/CppInterOp/CppInterOp.h index 534a8f45c..254619f9b 100644 --- a/include/CppInterOp/CppInterOp.h +++ b/include/CppInterOp/CppInterOp.h @@ -93,6 +93,7 @@ enum Operator : unsigned char { }; enum OperatorArity : unsigned char { kUnary = 1, kBinary, kBoth }; +enum Signedness : unsigned char { kSigned = 1, kUnsigned, kAny }; /// A class modeling function calls for functions produced by the interpreter /// in compiled code. It provides an information if we are calling a standard @@ -578,6 +579,22 @@ CPPINTEROP_API bool IsRecordType(TCppType_t type); /// Checks if the provided parameter is a Plain Old Data Type (POD). CPPINTEROP_API bool IsPODType(TCppType_t type); +/// Checks if type has an integer representation +CPPINTEROP_API bool IsIntegerType(TCppType_t type, + Signedness s = Signedness::kAny); + +/// Checks if type has a floating representation +CPPINTEROP_API bool IsFloatingType(TCppType_t type); + +/// Checks if two types are the equivalent +/// i.e. have the same canonical type +CPPINTEROP_API bool IsSameType(TCppType_t type_a, TCppType_t type_b); + +/// Checks if type is a void pointer +CPPINTEROP_API bool IsVoidPointerType(TCppType_t type); + +/// Get the type handle to the unqualified type +CPPINTEROP_API TCppType_t GetUnqualifiedType(TCppType_t type); /// Checks if type is a pointer CPPINTEROP_API bool IsPointerType(TCppType_t type); diff --git a/lib/CppInterOp/CppInterOp.cpp b/lib/CppInterOp/CppInterOp.cpp index b4dd7ad74..64ca83b12 100755 --- a/lib/CppInterOp/CppInterOp.cpp +++ b/lib/CppInterOp/CppInterOp.cpp @@ -1619,11 +1619,44 @@ bool IsPODType(TCppType_t type) { return QT.isPODType(getASTContext()); } +bool IsIntegerType(TCppType_t type, Signedness s) { + if (!type) + return false; + QualType QT = QualType::getFromOpaquePtr(type); + switch (s) { + case Signedness::kAny: + return QT->hasIntegerRepresentation(); + + case Signedness::kSigned: + return QT->hasSignedIntegerRepresentation(); + + case Signedness::kUnsigned: + return QT->hasUnsignedIntegerRepresentation(); + } + return false; +} + +bool IsFloatingType(TCppType_t type) { + QualType QT = QualType::getFromOpaquePtr(type); + return QT->hasFloatingRepresentation(); +} + +bool IsSameType(TCppType_t type_a, TCppType_t type_b) { + clang::QualType QT1 = clang::QualType::getFromOpaquePtr(type_a); + clang::QualType QT2 = clang::QualType::getFromOpaquePtr(type_b); + return getASTContext().hasSameType(QT1, QT2); +} + bool IsPointerType(TCppType_t type) { QualType QT = QualType::getFromOpaquePtr(type); return QT->isPointerType(); } +bool IsVoidPointerType(TCppType_t type) { + QualType QT = QualType::getFromOpaquePtr(type); + return QT->isVoidPointerType(); +} + TCppType_t GetPointeeType(TCppType_t type) { if (!IsPointerType(type)) return nullptr; @@ -1631,6 +1664,13 @@ TCppType_t GetPointeeType(TCppType_t type) { return QT->getPointeeType().getAsOpaquePtr(); } +TCppType_t GetUnqualifiedType(TCppType_t type) { + if (!type) + return nullptr; + QualType QT = QualType::getFromOpaquePtr(type); + return QT.getUnqualifiedType().getAsOpaquePtr(); +} + bool IsReferenceType(TCppType_t type) { QualType QT = QualType::getFromOpaquePtr(type); return QT->isReferenceType(); @@ -1666,6 +1706,8 @@ TCppType_t GetNonReferenceType(TCppType_t type) { } TCppType_t GetUnderlyingType(TCppType_t type) { + if (!type) + return nullptr; QualType QT = QualType::getFromOpaquePtr(type); QT = QT->getCanonicalTypeUnqualified(); diff --git a/unittests/CppInterOp/TypeReflectionTest.cpp b/unittests/CppInterOp/TypeReflectionTest.cpp index 26eb2d621..85f33221a 100644 --- a/unittests/CppInterOp/TypeReflectionTest.cpp +++ b/unittests/CppInterOp/TypeReflectionTest.cpp @@ -620,3 +620,107 @@ TEST(TypeReflectionTest, OperatorSpelling) { EXPECT_EQ(Cpp::GetOperatorFromSpelling("()"), Cpp::OP_Call); EXPECT_EQ(Cpp::GetOperatorFromSpelling("invalid"), Cpp::OP_None); } + +TEST(TypeReflectionTest, IntegerTypes) { + Cpp::CreateInterpreter(); + std::vector Decls; + std::string code = R"( + int a; + int *b; + double c; + enum A { x, y }; + A evar = x; + char k; + long int l; + unsigned int m; + unsigned long n; + )"; + + GetAllTopLevelDecls(code, Decls); + + // Signedness defaults to Any and returns true for both signed and unsigned + // types. + EXPECT_TRUE(Cpp::IsIntegerType(Cpp::GetVariableType(Decls[0]))); + EXPECT_FALSE(Cpp::IsIntegerType(Cpp::GetVariableType(Decls[1]))); + EXPECT_FALSE(Cpp::IsIntegerType(Cpp::GetVariableType(Decls[2]))); + EXPECT_TRUE(Cpp::IsIntegerType(Cpp::GetVariableType(Decls[4]))); + EXPECT_TRUE(Cpp::IsIntegerType(Cpp::GetVariableType(Decls[5]))); + EXPECT_TRUE(Cpp::IsIntegerType(Cpp::GetVariableType(Decls[6]))); + EXPECT_TRUE(Cpp::IsIntegerType(Cpp::GetVariableType(Decls[7]), + Cpp::Signedness::kUnsigned)); + EXPECT_FALSE(Cpp::IsIntegerType(Cpp::GetVariableType(Decls[8]), + Cpp::Signedness::kSigned)); +} + +TEST(TypeReflectionTest, VoidPtrType) { + Cpp::CreateInterpreter(); + std::vector Decls; + std::string code = R"( + class A {}; + using VoidPtrType = void*; + VoidPtrType a = nullptr; + void * b = nullptr; + A *pa = nullptr; + )"; + + GetAllTopLevelDecls(code, Decls); + + EXPECT_EQ(Cpp::GetTypeAsString(Cpp::GetVariableType(Decls[2])), + "VoidPtrType"); + EXPECT_TRUE(Cpp::IsVoidPointerType(Cpp::GetVariableType(Decls[2]))); + EXPECT_TRUE(Cpp::IsVoidPointerType(Cpp::GetVariableType(Decls[3]))); + EXPECT_FALSE(Cpp::IsVoidPointerType(Cpp::GetVariableType(Decls[4]))); +} + +TEST(TypeReflectionTest, IsSameType) { + Cpp::CreateInterpreter(); + std::vector Decls; + + std::string code = R"( + #include + #include + + typedef std::va_list VaListAlias; + std::va_list va1; + VaListAlias va2; + const int ci = 0; + int const ic = 0; + signed int si1 = 0; + int si2 = 0; + void *x; + )"; + + GetAllTopLevelDecls(code, Decls); + ASTContext& Ctxt = Interp->getCI()->getASTContext(); + Decls.assign(Decls.end() - 8, Decls.end()); + + EXPECT_TRUE( + Cpp::IsSameType(Cpp::GetType("bool"), Ctxt.BoolTy.getAsOpaquePtr())); + EXPECT_TRUE( + Cpp::IsSameType(Cpp::GetType("float"), Ctxt.FloatTy.getAsOpaquePtr())); + EXPECT_TRUE( + Cpp::IsSameType(Cpp::GetType("long"), Ctxt.LongTy.getAsOpaquePtr())); + EXPECT_TRUE(Cpp::IsSameType(Cpp::GetType("long long"), + Ctxt.LongLongTy.getAsOpaquePtr())); + EXPECT_TRUE( + Cpp::IsSameType(Cpp::GetType("short"), Ctxt.ShortTy.getAsOpaquePtr())); + EXPECT_TRUE(Cpp::IsSameType(Cpp::GetType("char"), + Ctxt.SignedCharTy.getAsOpaquePtr())); + EXPECT_TRUE(Cpp::IsSameType(Cpp::GetType("unsigned char"), + Ctxt.UnsignedCharTy.getAsOpaquePtr())); + EXPECT_TRUE(Cpp::IsSameType(Cpp::GetType("unsigned int"), + Ctxt.UnsignedIntTy.getAsOpaquePtr())); + +#if CLANG_VERSION_MAJOR > 16 || !defined(CPPINTEROP_USE_CLING) + EXPECT_TRUE(Cpp::IsSameType(Cpp::GetVariableType(Decls[7]), + Ctxt.VoidPtrTy.getAsOpaquePtr())); + + // Expect the typedef to std::va_list to be the same type + EXPECT_TRUE(Cpp::IsSameType(Cpp::GetVariableType(Decls[1]), + Cpp::GetVariableType(Decls[2]))); + EXPECT_TRUE(Cpp::IsSameType(Cpp::GetVariableType(Decls[3]), + Cpp::GetVariableType(Decls[4]))); + EXPECT_TRUE(Cpp::IsSameType(Cpp::GetVariableType(Decls[5]), + Cpp::GetVariableType(Decls[6]))); +#endif +}