Skip to content

Add type info interfaces motivated by numba #534

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions include/CppInterOp/CppInterOp.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Signedness s = Signedness::kAny);
Signedness *s = nullptr);

We should probably take that as an output argument because you can have an integer type that's of different signedness and is still an int type.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, what you describe will happen if the caller specifies signed or unsigned. I was of the opinion that if you call this by default (for kAny) it will return true if the qualtype has an integer representation which should cover the above scenario

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but if you don't know the underlying signedness you will get a false telling you that this is not an integral type. This assumes that you will call the function couple of times before you figure everything out. My proposal is to enable this in a single call.


/// 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);

Expand Down
42 changes: 42 additions & 0 deletions lib/CppInterOp/CppInterOp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1619,18 +1619,58 @@
return QT.isPODType(getASTContext());
}

bool IsIntegerType(TCppType_t type, Signedness s) {
if (!type)
return false;

Check warning on line 1624 in lib/CppInterOp/CppInterOp.cpp

View check run for this annotation

Codecov / codecov/patch

lib/CppInterOp/CppInterOp.cpp#L1624

Added line #L1624 was not covered by tests
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;

Check warning on line 1636 in lib/CppInterOp/CppInterOp.cpp

View check run for this annotation

Codecov / codecov/patch

lib/CppInterOp/CppInterOp.cpp#L1636

Added line #L1636 was not covered by tests
}

bool IsFloatingType(TCppType_t type) {
QualType QT = QualType::getFromOpaquePtr(type);
return QT->hasFloatingRepresentation();

Check warning on line 1641 in lib/CppInterOp/CppInterOp.cpp

View check run for this annotation

Codecov / codecov/patch

lib/CppInterOp/CppInterOp.cpp#L1639-L1641

Added lines #L1639 - L1641 were not covered by tests
}

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;
QualType QT = QualType::getFromOpaquePtr(type);
return QT->getPointeeType().getAsOpaquePtr();
}

TCppType_t GetUnqualifiedType(TCppType_t type) {
if (!type)
return nullptr;
QualType QT = QualType::getFromOpaquePtr(type);
return QT.getUnqualifiedType().getAsOpaquePtr();

Check warning on line 1671 in lib/CppInterOp/CppInterOp.cpp

View check run for this annotation

Codecov / codecov/patch

lib/CppInterOp/CppInterOp.cpp#L1667-L1671

Added lines #L1667 - L1671 were not covered by tests
}

bool IsReferenceType(TCppType_t type) {
QualType QT = QualType::getFromOpaquePtr(type);
return QT->isReferenceType();
Expand Down Expand Up @@ -1666,6 +1706,8 @@
}

TCppType_t GetUnderlyingType(TCppType_t type) {
if (!type)
return nullptr;

Check warning on line 1710 in lib/CppInterOp/CppInterOp.cpp

View check run for this annotation

Codecov / codecov/patch

lib/CppInterOp/CppInterOp.cpp#L1710

Added line #L1710 was not covered by tests
QualType QT = QualType::getFromOpaquePtr(type);
QT = QT->getCanonicalTypeUnqualified();

Expand Down
104 changes: 104 additions & 0 deletions unittests/CppInterOp/TypeReflectionTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<Decl*> 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<Decl*> 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<Decl*> Decls;

std::string code = R"(
#include <cstdarg>
#include <iostream>

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
}
Loading