Skip to content

Add error propagation to interpreter creation logic #553

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

Merged
merged 7 commits into from
Apr 17, 2025
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
11 changes: 9 additions & 2 deletions lib/Interpreter/CXCppInterOp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -271,8 +271,15 @@ static inline compat::Interpreter* getInterpreter(const CXInterpreterImpl* I) {
}

CXInterpreter clang_createInterpreter(const char* const* argv, int argc) {
auto* I = new CXInterpreterImpl(); // NOLINT(*-owning-memory)
auto I = std::make_unique<CXInterpreterImpl>();
#ifdef CPPINTEROP_USE_CLING
I->Interp = std::make_unique<compat::Interpreter>(argc, argv);
#else
I->Interp = compat::Interpreter::create(argc, argv);
if (!I->Interp) {
return nullptr;
}
#endif
// create a bridge between CXTranslationUnit and clang::Interpreter
// auto AU = std::make_unique<ASTUnit>(false);
// AU->FileMgr = I->Interp->getCompilerInstance().getFileManager();
Expand All @@ -281,7 +288,7 @@ CXInterpreter clang_createInterpreter(const char* const* argv, int argc) {
// AU->Ctx = &I->Interp->getSema().getASTContext();
// I->TU.reset(MakeCXTranslationUnit(static_cast<CIndexer*>(clang_createIndex(0,
// 0)), AU));
return I;
return I.release();
}

CXInterpreter clang_createInterpreterFromRawPtr(TInterp_t I) {
Expand Down
4 changes: 3 additions & 1 deletion lib/Interpreter/Compatibility.h
Original file line number Diff line number Diff line change
Expand Up @@ -294,9 +294,11 @@ createClangInterpreter(std::vector<const char*>& args) {
return nullptr;
}
if (CudaEnabled) {
if (auto Err = (*innerOrErr)->LoadDynamicLibrary("libcudart.so"))
if (auto Err = (*innerOrErr)->LoadDynamicLibrary("libcudart.so")) {
llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(),
"Failed load libcudart.so runtime:");
return nullptr;
}
}

return std::move(*innerOrErr);
Expand Down
8 changes: 8 additions & 0 deletions lib/Interpreter/CppInterOp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2957,7 +2957,15 @@ namespace Cpp {
std::back_inserter(ClingArgv),
[&](const std::string& str) { return str.c_str(); });

#ifdef CPPINTEROP_USE_CLING
auto I = new compat::Interpreter(ClingArgv.size(), &ClingArgv[0]);
#else
auto Interp = compat::Interpreter::create(
static_cast<int>(ClingArgv.size()), ClingArgv.data());
if (!Interp)
return nullptr;
auto* I = Interp.release();
#endif

// Honor -mllvm.
//
Expand Down
24 changes: 19 additions & 5 deletions lib/Interpreter/CppInterOpInterpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#if CLANG_VERSION_MAJOR >= 19
#include "clang/Sema/Redeclaration.h"
#endif
#include "clang/Serialization/ModuleFileExtension.h"

#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallSet.h"
Expand All @@ -34,6 +35,10 @@
#include "llvm/Support/Compiler.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/raw_ostream.h"

#include <utility>
#include <vector>

namespace clang {
class CompilerInstance;
Expand Down Expand Up @@ -136,19 +141,28 @@ class Interpreter {
private:
std::unique_ptr<clang::Interpreter> inner;

Interpreter(std::unique_ptr<clang::Interpreter> CI) : inner(std::move(CI)) {}

public:
Interpreter(int argc, const char* const* argv, const char* llvmdir = 0,
const std::vector<std::shared_ptr<clang::ModuleFileExtension>>&
moduleExtensions = {},
void* extraLibHandle = 0, bool noRuntime = true) {
static std::unique_ptr<Interpreter>
create(int argc, const char* const* argv, const char* llvmdir = nullptr,
const std::vector<std::shared_ptr<clang::ModuleFileExtension>>&
moduleExtensions = {},
void* extraLibHandle = nullptr, bool noRuntime = true) {
// Initialize all targets (required for device offloading)
llvm::InitializeAllTargetInfos();
llvm::InitializeAllTargets();
llvm::InitializeAllTargetMCs();
llvm::InitializeAllAsmPrinters();

std::vector<const char*> vargs(argv + 1, argv + argc);
inner = compat::createClangInterpreter(vargs);
auto CI = compat::createClangInterpreter(vargs);
if (!CI) {
llvm::errs() << "Interpreter creation failed\n";
return nullptr;
}

return std::unique_ptr<Interpreter>(new Interpreter(std::move(CI)));
}

~Interpreter() {}
Expand Down
8 changes: 5 additions & 3 deletions unittests/CppInterOp/CUDATest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ static bool HasCudaSDK() {
// FIXME: Enable this for cling.
return false;
#endif // CLANG_VERSION_MAJOR < 16
Cpp::CreateInterpreter({}, {"--cuda"});
if (!Cpp::CreateInterpreter({}, {"--cuda"}))
return false;
return Cpp::Declare("__global__ void test_func() {}"
"test_func<<<1,1>>>();") == 0;
};
Expand All @@ -30,7 +31,8 @@ static bool HasCudaRuntime() {
if (!HasCudaSDK())
return false;

Cpp::CreateInterpreter({}, {"--cuda"});
if (!Cpp::CreateInterpreter({}, {"--cuda"}))
return false;
if (Cpp::Declare("__global__ void test_func() {}"
"test_func<<<1,1>>>();"))
return false;
Expand Down Expand Up @@ -74,4 +76,4 @@ TEST(CUDATest, CUDARuntime) {
GTEST_SKIP() << "Skipping CUDA tests as CUDA runtime not found";

EXPECT_TRUE(HasCudaRuntime());
}
}
19 changes: 19 additions & 0 deletions unittests/CppInterOp/InterpreterTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,25 @@ TEST(InterpreterTest, CreateInterpreter) {
#endif
}

#ifndef CPPINTEROP_USE_CLING
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there a reason why we are not doing this test for Cling?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

No clue,

I see no C-API tests done through cling and that's what I am trying to replicate. Maybe @Gnimuc can educate us here.

Copy link
Contributor

Choose a reason for hiding this comment

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

The original purpose of adding the C API is to support multiple instances. REPL and Cling are two backends for creating compiler instances. REPL is based on clang::Interpreter from the upstream llvm-project.

I think there is no plan to upstream Cling to the llvm-project, and I didn't check Cling's compatibility when I added those C-API tests.

Copy link
Contributor

Choose a reason for hiding this comment

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

You can build, CppInterOp by providing cling and it will embed cling in it. If that’s not too much of work maybe we can support that, shouldn’t hurt.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Can we have a dedicated PR for that ?

I'll say the purpose of this PR is complete now.

If that’s not too much of work maybe we can support that, shouldn’t hurt.

@Gnimuc would you like to give this an attempt ?

TEST(InterpreterTest, CreateInterpreterCAPI) {
const char* argv[] = {"-std=c++17"};
auto *CXI = clang_createInterpreter(argv, 1);
auto CLI = clang_Interpreter_getClangInterpreter(CXI);
EXPECT_TRUE(CLI);
clang_Interpreter_dispose(CXI);
}

TEST(InterpreterTest, CreateInterpreterCAPIFailure) {
#ifdef _WIN32
GTEST_SKIP() << "Disabled on Windows. Needs fixing.";
#endif
const char* argv[] = {"-fsyntax-only", "-Xclang", "-invalid-plugin"};
auto *CXI = clang_createInterpreter(argv, 3);
EXPECT_EQ(CXI, nullptr);
}
#endif

#ifdef LLVM_BINARY_DIR
TEST(InterpreterTest, DetectResourceDir) {
#ifdef EMSCRIPTEN
Expand Down
Loading