Skip to content
Merged
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
3 changes: 3 additions & 0 deletions include/CppInterOp/CppInterOp.h
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,9 @@ CPPINTEROP_API bool IsDestructor(TCppConstFunction_t method);
/// Checks if the provided parameter is a 'Static' method.
CPPINTEROP_API bool IsStaticMethod(TCppConstFunction_t method);

/// Checks if the provided constructor or conversion operator is explicit
CPPINTEROP_API bool IsExplicit(TCppConstFunction_t method);

///\returns the address of the function given its potentially mangled name.
CPPINTEROP_API TCppFuncAddr_t GetFunctionAddress(const char* mangled_name);

Expand Down
1 change: 1 addition & 0 deletions include/CppInterOp/Dispatch.h
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ extern "C" CPPINTEROP_API CppFnPtrTy CppGetProcAddress(const char* procname);
DISPATCH_API(BeginStdStreamCapture, \
decltype(&CppImpl::BeginStdStreamCapture)) \
DISPATCH_API(GetDoxygenComment, decltype(&CppImpl::GetDoxygenComment)) \
DISPATCH_API(IsExplicit, decltype(&CppImpl::IsExplicit)) \
DISPATCH_API(MakeFunctionCallable, \
CppImpl::JitCall (*)(CppImpl::TCppConstFunction_t)) \
DISPATCH_API(GetFunctionAddress, \
Expand Down
21 changes: 21 additions & 0 deletions lib/CppInterOp/CppInterOp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1409,6 +1409,27 @@ bool IsStaticMethod(TCppConstFunction_t method) {
return false;
}

bool IsExplicit(TCppConstFunction_t method) {
if (!method)
return false;

const auto* D = static_cast<const Decl*>(method);

if (const auto* FTD = llvm::dyn_cast_or_null<FunctionTemplateDecl>(D))
D = FTD->getTemplatedDecl();

if (const auto* CD = llvm::dyn_cast_or_null<CXXConstructorDecl>(D))
return CD->isExplicit();

if (const auto* CD = llvm::dyn_cast_or_null<CXXConversionDecl>(D))
return CD->isExplicit();

if (const auto* DGD = llvm::dyn_cast_or_null<CXXDeductionGuideDecl>(D))
return DGD->isExplicit();

return false;
}

TCppFuncAddr_t GetFunctionAddress(const char* mangled_name) {
auto& I = getInterp();
auto FDAorErr = compat::getSymbolAddress(I, mangled_name);
Expand Down
135 changes: 135 additions & 0 deletions unittests/CppInterOp/FunctionReflectionTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2900,3 +2900,138 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_FailingTest1) {
EXPECT_FALSE(Cpp::Declare("int x = 1;" DFLT_FALSE));
EXPECT_FALSE(Cpp::Declare("int y = x;" DFLT_FALSE));
}

TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_IsExplicit) {
std::vector<Decl*> Decls;
std::vector<Decl*> SubDecls;
std::string code = R"(
class C {
public:
C() {}
explicit C(int) {}
C(const C&) {}
explicit C(C&&) {}

operator int() { return 0; }
explicit operator bool() { return true; }

void regular_method() {}
private:
explicit C(double) {}
};
)";

GetAllTopLevelDecls(code, Decls);
GetAllSubDecls(Decls[0], SubDecls);

// constructors
EXPECT_FALSE(Cpp::IsExplicit(SubDecls[2])); // C()
EXPECT_TRUE(Cpp::IsExplicit(SubDecls[3])); // explicit C(int)
EXPECT_FALSE(Cpp::IsExplicit(SubDecls[4])); // C(const C&) copy ctor
EXPECT_TRUE(Cpp::IsExplicit(SubDecls[5])); // explicit C(C&&) move ctor

// conversion operators
EXPECT_FALSE(Cpp::IsExplicit(SubDecls[6])); // operator int()
EXPECT_TRUE(Cpp::IsExplicit(SubDecls[7])); // explicit operator bool()
EXPECT_FALSE(Cpp::IsExplicit(SubDecls[8])); // regular_method()
EXPECT_TRUE(Cpp::IsExplicit(SubDecls[10])); // private explicit C(double)
EXPECT_FALSE(Cpp::IsExplicit(Decls[0]));
EXPECT_FALSE(Cpp::IsExplicit(nullptr));
}
TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_IsExplicitTemplated) {
std::vector<Decl*> Decls;
std::vector<Decl*> SubDecls;
std::string code = R"(
class T {
public:
T() = delete;

template<typename U>
T(U) {}

template<typename U>
explicit T(U, int) {}

template<typename U>
operator U() { return U{}; }

template<typename U>
explicit operator U*() { return nullptr; }
};
)";

GetAllTopLevelDecls(code, Decls);
GetAllSubDecls(Decls[0], SubDecls);

int implicitCtorCount = 0;
int explicitCtorCount = 0;
int implicitConvCount = 0;
int explicitConvCount = 0;

for (auto* decl : SubDecls) {
// skip deleted constructors
if (auto* CD = llvm::dyn_cast_or_null<CXXConstructorDecl>(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

warning: no header providing "llvm::dyn_cast_or_null" is directly included [misc-include-cleaner]

unittests/CppInterOp/FunctionReflectionTest.cpp:15:

- #include <string>
+ #include <llvm/Support/Casting.h>
+ #include <string>

static_cast<clang::Decl*>(decl))) {
if (CD->isDeleted())
continue;
}

if (Cpp::IsConstructor(decl)) {
if (Cpp::IsExplicit(decl))
explicitCtorCount++;
else
implicitCtorCount++;
} else {
// conversion operator
auto* D = static_cast<clang::Decl*>(decl);
if (auto* FTD = llvm::dyn_cast_or_null<FunctionTemplateDecl>(D))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

warning: no header providing "clang::FunctionTemplateDecl" is directly included [misc-include-cleaner]

      if (auto* FTD = llvm::dyn_cast_or_null<FunctionTemplateDecl>(D))
                                             ^

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

warning: no header providing "llvm::dyn_cast_or_null" is directly included [misc-include-cleaner]

      if (auto* FTD = llvm::dyn_cast_or_null<FunctionTemplateDecl>(D))
                            ^

D = FTD->getTemplatedDecl();

if (llvm::isa<CXXConversionDecl>(D)) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

warning: no header providing "clang::CXXConversionDecl" is directly included [misc-include-cleaner]

      if (llvm::isa<CXXConversionDecl>(D)) {
                    ^

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

warning: no header providing "llvm::isa" is directly included [misc-include-cleaner]

      if (llvm::isa<CXXConversionDecl>(D)) {
                ^

if (Cpp::IsExplicit(decl))
explicitConvCount++;
else
implicitConvCount++;
}
}
}

EXPECT_EQ(implicitCtorCount, 1); // T(U)
EXPECT_EQ(explicitCtorCount, 1); // explicit T(U, int)
EXPECT_EQ(implicitConvCount, 1); // operator U()
EXPECT_EQ(explicitConvCount, 1); // explicit operator U*()
}

TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_IsExplicitDeductionGuide) {
// Deduction guides are a C++17 feature
std::vector<const char*> interpreter_args = {"-include", "new", "-std=c++17"};
Cpp::CreateInterpreter(interpreter_args, {});

Interp->declare(R"(
template<typename T>
struct Wrapper {
T value;
Wrapper(T v) : value(v) {}
};
template<typename T>
explicit Wrapper(T*) -> Wrapper<T*>;
)");

auto* TUD = Interp->getCI()->getASTContext().getTranslationUnitDecl();
bool foundExplicitGuide = false;
Decl* guide = nullptr;
for (auto* D : TUD->decls()) {
if (Cpp::IsExplicit(D)) {
foundExplicitGuide = true;
guide = D;
break;
}
}

EXPECT_TRUE(foundExplicitGuide);
EXPECT_TRUE(guide != nullptr);
EXPECT_EQ(Cpp::GetFunctionSignature(guide),
"explicit Wrapper(T *) -> Wrapper<T *>");
EXPECT_EQ(Cpp::GetTypeAsString(Cpp::GetFunctionReturnType(guide)),
"Wrapper<T *>");
}
Loading