xref: /llvm-project/clang/tools/clang-repl/ClangRepl.cpp (revision 8358437bbb5b06d9aebc2940475a5a4d86c091c9)
192f9852fSVassil Vassilev //===--- tools/clang-repl/ClangRepl.cpp - clang-repl - the Clang REPL -----===//
292f9852fSVassil Vassilev //
392f9852fSVassil Vassilev // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
492f9852fSVassil Vassilev // See https://llvm.org/LICENSE.txt for license information.
592f9852fSVassil Vassilev // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
692f9852fSVassil Vassilev //
792f9852fSVassil Vassilev //===----------------------------------------------------------------------===//
892f9852fSVassil Vassilev //
992f9852fSVassil Vassilev //  This file implements a REPL tool on top of clang.
1092f9852fSVassil Vassilev //
1192f9852fSVassil Vassilev //===----------------------------------------------------------------------===//
1292f9852fSVassil Vassilev 
1392f9852fSVassil Vassilev #include "clang/Basic/Diagnostic.h"
1492f9852fSVassil Vassilev #include "clang/Frontend/CompilerInstance.h"
1592f9852fSVassil Vassilev #include "clang/Frontend/FrontendDiagnostic.h"
1679af92bbSFred Fu #include "clang/Interpreter/CodeCompletion.h"
1792f9852fSVassil Vassilev #include "clang/Interpreter/Interpreter.h"
1835b366acSFred Fu #include "clang/Lex/Preprocessor.h"
1935b366acSFred Fu #include "clang/Sema/Sema.h"
2092f9852fSVassil Vassilev 
2192f9852fSVassil Vassilev #include "llvm/ExecutionEngine/Orc/LLJIT.h"
2292f9852fSVassil Vassilev #include "llvm/LineEditor/LineEditor.h"
2392f9852fSVassil Vassilev #include "llvm/Support/CommandLine.h"
2492f9852fSVassil Vassilev #include "llvm/Support/ManagedStatic.h" // llvm_shutdown
2592f9852fSVassil Vassilev #include "llvm/Support/Signals.h"
26ddeab07cSAnubhab Ghosh #include "llvm/Support/TargetSelect.h"
27a1580d7bSKazu Hirata #include <optional>
2892f9852fSVassil Vassilev 
2953a87b4aSLang Hames // Disable LSan for this test.
3053a87b4aSLang Hames // FIXME: Re-enable once we can assume GCC 13.2 or higher.
3153a87b4aSLang Hames // https://llvm.org/github.com/llvm/llvm-project/issues/67586.
3253a87b4aSLang Hames #if LLVM_ADDRESS_SANITIZER_BUILD || LLVM_HWADDRESS_SANITIZER_BUILD
3353a87b4aSLang Hames #include <sanitizer/lsan_interface.h>
3453a87b4aSLang Hames LLVM_ATTRIBUTE_USED int __lsan_is_turned_off() { return 1; }
3553a87b4aSLang Hames #endif
3653a87b4aSLang Hames 
37ddeab07cSAnubhab Ghosh static llvm::cl::opt<bool> CudaEnabled("cuda", llvm::cl::Hidden);
38ddeab07cSAnubhab Ghosh static llvm::cl::opt<std::string> CudaPath("cuda-path", llvm::cl::Hidden);
39ddeab07cSAnubhab Ghosh static llvm::cl::opt<std::string> OffloadArch("offload-arch", llvm::cl::Hidden);
40ebb35088SSahilPatidar 
4192f9852fSVassil Vassilev static llvm::cl::list<std::string>
42d86a206fSFangrui Song     ClangArgs("Xcc",
4392f9852fSVassil Vassilev               llvm::cl::desc("Argument to pass to the CompilerInvocation"),
4492f9852fSVassil Vassilev               llvm::cl::CommaSeparated);
4592f9852fSVassil Vassilev static llvm::cl::opt<bool> OptHostSupportsJit("host-supports-jit",
4692f9852fSVassil Vassilev                                               llvm::cl::Hidden);
47f01d45c3SVassil Vassilev static llvm::cl::list<std::string> OptInputs(llvm::cl::Positional,
48f01d45c3SVassil Vassilev                                              llvm::cl::desc("[code to run]"));
4992f9852fSVassil Vassilev 
50e463b697SSimon Pilgrim static void LLVMErrorHandler(void *UserData, const char *Message,
5192f9852fSVassil Vassilev                              bool GenCrashDiag) {
5292f9852fSVassil Vassilev   auto &Diags = *static_cast<clang::DiagnosticsEngine *>(UserData);
5392f9852fSVassil Vassilev 
5492f9852fSVassil Vassilev   Diags.Report(clang::diag::err_fe_error_backend) << Message;
5592f9852fSVassil Vassilev 
5692f9852fSVassil Vassilev   // Run the interrupt handlers to make sure any special cleanups get done, in
5792f9852fSVassil Vassilev   // particular that we remove files registered with RemoveFileOnSignal.
5892f9852fSVassil Vassilev   llvm::sys::RunInterruptHandlers();
5992f9852fSVassil Vassilev 
6092f9852fSVassil Vassilev   // We cannot recover from llvm errors.  When reporting a fatal error, exit
6192f9852fSVassil Vassilev   // with status 70 to generate crash diagnostics.  For BSD systems this is
6292f9852fSVassil Vassilev   // defined as an internal software error. Otherwise, exit with status 1.
6392f9852fSVassil Vassilev 
6492f9852fSVassil Vassilev   exit(GenCrashDiag ? 70 : 1);
6592f9852fSVassil Vassilev }
6692f9852fSVassil Vassilev 
67946c45a4STapasweni Pathak // If we are running with -verify a reported has to be returned as unsuccess.
68946c45a4STapasweni Pathak // This is relevant especially for the test suite.
699caee577SJun Zhang static int checkDiagErrors(const clang::CompilerInstance *CI, bool HasError) {
70946c45a4STapasweni Pathak   unsigned Errs = CI->getDiagnostics().getClient()->getNumErrors();
71946c45a4STapasweni Pathak   if (CI->getDiagnosticOpts().VerifyDiagnostics) {
72946c45a4STapasweni Pathak     // If there was an error that came from the verifier we must return 1 as
73946c45a4STapasweni Pathak     // an exit code for the process. This will make the test fail as expected.
74946c45a4STapasweni Pathak     clang::DiagnosticConsumer *Client = CI->getDiagnostics().getClient();
75946c45a4STapasweni Pathak     Client->EndSourceFile();
76946c45a4STapasweni Pathak     Errs = Client->getNumErrors();
77946c45a4STapasweni Pathak 
78946c45a4STapasweni Pathak     // The interpreter expects BeginSourceFile/EndSourceFiles to be balanced.
79946c45a4STapasweni Pathak     Client->BeginSourceFile(CI->getLangOpts(), &CI->getPreprocessor());
80946c45a4STapasweni Pathak   }
819caee577SJun Zhang   return (Errs || HasError) ? EXIT_FAILURE : EXIT_SUCCESS;
82946c45a4STapasweni Pathak }
83946c45a4STapasweni Pathak 
8479af92bbSFred Fu struct ReplListCompleter {
8579af92bbSFred Fu   clang::IncrementalCompilerBuilder &CB;
8679af92bbSFred Fu   clang::Interpreter &MainInterp;
8779af92bbSFred Fu   ReplListCompleter(clang::IncrementalCompilerBuilder &CB,
8879af92bbSFred Fu                     clang::Interpreter &Interp)
8979af92bbSFred Fu       : CB(CB), MainInterp(Interp){};
9079af92bbSFred Fu 
9179af92bbSFred Fu   std::vector<llvm::LineEditor::Completion> operator()(llvm::StringRef Buffer,
9279af92bbSFred Fu                                                        size_t Pos) const;
9379af92bbSFred Fu   std::vector<llvm::LineEditor::Completion>
9479af92bbSFred Fu   operator()(llvm::StringRef Buffer, size_t Pos, llvm::Error &ErrRes) const;
9579af92bbSFred Fu };
9679af92bbSFred Fu 
9779af92bbSFred Fu std::vector<llvm::LineEditor::Completion>
9879af92bbSFred Fu ReplListCompleter::operator()(llvm::StringRef Buffer, size_t Pos) const {
9979af92bbSFred Fu   auto Err = llvm::Error::success();
10079af92bbSFred Fu   auto res = (*this)(Buffer, Pos, Err);
10179af92bbSFred Fu   if (Err)
10279af92bbSFred Fu     llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: ");
10379af92bbSFred Fu   return res;
10479af92bbSFred Fu }
10579af92bbSFred Fu 
10679af92bbSFred Fu std::vector<llvm::LineEditor::Completion>
10779af92bbSFred Fu ReplListCompleter::operator()(llvm::StringRef Buffer, size_t Pos,
10879af92bbSFred Fu                               llvm::Error &ErrRes) const {
10979af92bbSFred Fu   std::vector<llvm::LineEditor::Completion> Comps;
11079af92bbSFred Fu   std::vector<std::string> Results;
11179af92bbSFred Fu 
11279af92bbSFred Fu   auto CI = CB.CreateCpp();
11379af92bbSFred Fu   if (auto Err = CI.takeError()) {
11479af92bbSFred Fu     ErrRes = std::move(Err);
11579af92bbSFred Fu     return {};
11679af92bbSFred Fu   }
11779af92bbSFred Fu 
11879af92bbSFred Fu   size_t Lines =
11979af92bbSFred Fu       std::count(Buffer.begin(), std::next(Buffer.begin(), Pos), '\n') + 1;
12079af92bbSFred Fu   auto Interp = clang::Interpreter::create(std::move(*CI));
12179af92bbSFred Fu 
12279af92bbSFred Fu   if (auto Err = Interp.takeError()) {
12379af92bbSFred Fu     // log the error and returns an empty vector;
12479af92bbSFred Fu     ErrRes = std::move(Err);
12579af92bbSFred Fu 
12679af92bbSFred Fu     return {};
12779af92bbSFred Fu   }
12835b366acSFred Fu   auto *MainCI = (*Interp)->getCompilerInstance();
12935b366acSFred Fu   auto CC = clang::ReplCodeCompleter();
13035b366acSFred Fu   CC.codeComplete(MainCI, Buffer, Lines, Pos + 1,
13135b366acSFred Fu                   MainInterp.getCompilerInstance(), Results);
13279af92bbSFred Fu   for (auto c : Results) {
13335b366acSFred Fu     if (c.find(CC.Prefix) == 0)
13435b366acSFred Fu       Comps.push_back(
13535b366acSFred Fu           llvm::LineEditor::Completion(c.substr(CC.Prefix.size()), c));
13679af92bbSFred Fu   }
13779af92bbSFred Fu   return Comps;
13879af92bbSFred Fu }
13979af92bbSFred Fu 
14092f9852fSVassil Vassilev llvm::ExitOnError ExitOnErr;
14192f9852fSVassil Vassilev int main(int argc, const char **argv) {
142*8358437bSMészáros Gergely   llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
143*8358437bSMészáros Gergely 
14492f9852fSVassil Vassilev   ExitOnErr.setBanner("clang-repl: ");
14592f9852fSVassil Vassilev   llvm::cl::ParseCommandLineOptions(argc, argv);
14692f9852fSVassil Vassilev 
147c619d4f8SSunho Kim   llvm::llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
148c619d4f8SSunho Kim 
14992f9852fSVassil Vassilev   std::vector<const char *> ClangArgv(ClangArgs.size());
15092f9852fSVassil Vassilev   std::transform(ClangArgs.begin(), ClangArgs.end(), ClangArgv.begin(),
15192f9852fSVassil Vassilev                  [](const std::string &s) -> const char * { return s.data(); });
152ddeab07cSAnubhab Ghosh   // Initialize all targets (required for device offloading)
153ddeab07cSAnubhab Ghosh   llvm::InitializeAllTargetInfos();
154ddeab07cSAnubhab Ghosh   llvm::InitializeAllTargets();
155ddeab07cSAnubhab Ghosh   llvm::InitializeAllTargetMCs();
156ddeab07cSAnubhab Ghosh   llvm::InitializeAllAsmPrinters();
157cbdc86e4SAndrew V. Teylu   llvm::InitializeAllAsmParsers();
15892f9852fSVassil Vassilev 
15992f9852fSVassil Vassilev   if (OptHostSupportsJit) {
16061b0f12dSLang Hames     auto J = llvm::orc::LLJITBuilder().create();
16192f9852fSVassil Vassilev     if (J)
16292f9852fSVassil Vassilev       llvm::outs() << "true\n";
16392f9852fSVassil Vassilev     else {
16492f9852fSVassil Vassilev       llvm::consumeError(J.takeError());
16592f9852fSVassil Vassilev       llvm::outs() << "false\n";
16692f9852fSVassil Vassilev     }
16792f9852fSVassil Vassilev     return 0;
16892f9852fSVassil Vassilev   }
16992f9852fSVassil Vassilev 
170ddeab07cSAnubhab Ghosh   clang::IncrementalCompilerBuilder CB;
171ddeab07cSAnubhab Ghosh   CB.SetCompilerArgs(ClangArgv);
172ddeab07cSAnubhab Ghosh 
173ddeab07cSAnubhab Ghosh   std::unique_ptr<clang::CompilerInstance> DeviceCI;
174ddeab07cSAnubhab Ghosh   if (CudaEnabled) {
175ddeab07cSAnubhab Ghosh     if (!CudaPath.empty())
176ddeab07cSAnubhab Ghosh       CB.SetCudaSDK(CudaPath);
177ddeab07cSAnubhab Ghosh 
178ddeab07cSAnubhab Ghosh     if (OffloadArch.empty()) {
179ddeab07cSAnubhab Ghosh       OffloadArch = "sm_35";
180ddeab07cSAnubhab Ghosh     }
181ddeab07cSAnubhab Ghosh     CB.SetOffloadArch(OffloadArch);
182ddeab07cSAnubhab Ghosh 
183ddeab07cSAnubhab Ghosh     DeviceCI = ExitOnErr(CB.CreateCudaDevice());
184ddeab07cSAnubhab Ghosh   }
185ddeab07cSAnubhab Ghosh 
18692f9852fSVassil Vassilev   // FIXME: Investigate if we could use runToolOnCodeWithArgs from tooling. It
18792f9852fSVassil Vassilev   // can replace the boilerplate code for creation of the compiler instance.
188ddeab07cSAnubhab Ghosh   std::unique_ptr<clang::CompilerInstance> CI;
189ddeab07cSAnubhab Ghosh   if (CudaEnabled) {
190ddeab07cSAnubhab Ghosh     CI = ExitOnErr(CB.CreateCudaHost());
191ddeab07cSAnubhab Ghosh   } else {
192ddeab07cSAnubhab Ghosh     CI = ExitOnErr(CB.CreateCpp());
193ddeab07cSAnubhab Ghosh   }
19492f9852fSVassil Vassilev 
19592f9852fSVassil Vassilev   // Set an error handler, so that any LLVM backend diagnostics go through our
19692f9852fSVassil Vassilev   // error handler.
19792f9852fSVassil Vassilev   llvm::install_fatal_error_handler(LLVMErrorHandler,
19892f9852fSVassil Vassilev                                     static_cast<void *>(&CI->getDiagnostics()));
19992f9852fSVassil Vassilev 
200f4f9ad0fSVassil Vassilev   // Load any requested plugins.
201f4f9ad0fSVassil Vassilev   CI->LoadRequestedPlugins();
202ddeab07cSAnubhab Ghosh   if (CudaEnabled)
203ddeab07cSAnubhab Ghosh     DeviceCI->LoadRequestedPlugins();
204f4f9ad0fSVassil Vassilev 
205ddeab07cSAnubhab Ghosh   std::unique_ptr<clang::Interpreter> Interp;
20679af92bbSFred Fu 
207ddeab07cSAnubhab Ghosh   if (CudaEnabled) {
208ddeab07cSAnubhab Ghosh     Interp = ExitOnErr(
209ddeab07cSAnubhab Ghosh         clang::Interpreter::createWithCUDA(std::move(CI), std::move(DeviceCI)));
210ddeab07cSAnubhab Ghosh 
211ddeab07cSAnubhab Ghosh     if (CudaPath.empty()) {
212ddeab07cSAnubhab Ghosh       ExitOnErr(Interp->LoadDynamicLibrary("libcudart.so"));
213ddeab07cSAnubhab Ghosh     } else {
214ddeab07cSAnubhab Ghosh       auto CudaRuntimeLibPath = CudaPath + "/lib/libcudart.so";
215ddeab07cSAnubhab Ghosh       ExitOnErr(Interp->LoadDynamicLibrary(CudaRuntimeLibPath.c_str()));
216ddeab07cSAnubhab Ghosh     }
217ddeab07cSAnubhab Ghosh   } else
218ddeab07cSAnubhab Ghosh     Interp = ExitOnErr(clang::Interpreter::create(std::move(CI)));
219ddeab07cSAnubhab Ghosh 
2209caee577SJun Zhang   bool HasError = false;
2219caee577SJun Zhang 
222fd5f06ebSVassil Vassilev   for (const std::string &input : OptInputs) {
223fd5f06ebSVassil Vassilev     if (auto Err = Interp->ParseAndExecute(input)) {
224fd5f06ebSVassil Vassilev       llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: ");
225fd5f06ebSVassil Vassilev       HasError = true;
226fd5f06ebSVassil Vassilev     }
227fd5f06ebSVassil Vassilev   }
228fd5f06ebSVassil Vassilev 
229f01d45c3SVassil Vassilev   if (OptInputs.empty()) {
23092f9852fSVassil Vassilev     llvm::LineEditor LE("clang-repl");
231c96c5edbSVassil Vassilev     std::string Input;
23279af92bbSFred Fu     LE.setListCompleter(ReplListCompleter(CB, *Interp));
2336ad0788cSKazu Hirata     while (std::optional<std::string> Line = LE.readLine()) {
234c96c5edbSVassil Vassilev       llvm::StringRef L = *Line;
235c96c5edbSVassil Vassilev       L = L.trim();
236f3dcc235SKazu Hirata       if (L.ends_with("\\")) {
237c96c5edbSVassil Vassilev         Input += L.drop_back(1);
23896ae7c4fSVassil Vassilev         // If it is a preprocessor directive, new lines matter.
23996ae7c4fSVassil Vassilev         if (L.starts_with('#'))
24096ae7c4fSVassil Vassilev           Input += "\n";
241c96c5edbSVassil Vassilev         LE.setPrompt("clang-repl...   ");
242c96c5edbSVassil Vassilev         continue;
243c96c5edbSVassil Vassilev       }
244c96c5edbSVassil Vassilev 
245c96c5edbSVassil Vassilev       Input += L;
246c96c5edbSVassil Vassilev       if (Input == R"(%quit)") {
24792f9852fSVassil Vassilev         break;
24879af92bbSFred Fu       }
24979af92bbSFred Fu       if (Input == R"(%undo)") {
250fd5f06ebSVassil Vassilev         if (auto Err = Interp->Undo())
251dea5a9ccSJun Zhang           llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: ");
252c96c5edbSVassil Vassilev       } else if (Input.rfind("%lib ", 0) == 0) {
253fd5f06ebSVassil Vassilev         if (auto Err = Interp->LoadDynamicLibrary(Input.data() + 5))
254d978730dSAnubhab Ghosh           llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: ");
255c96c5edbSVassil Vassilev       } else if (auto Err = Interp->ParseAndExecute(Input)) {
256c96c5edbSVassil Vassilev         llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: ");
257d978730dSAnubhab Ghosh       }
258dea5a9ccSJun Zhang 
259c96c5edbSVassil Vassilev       Input = "";
260c96c5edbSVassil Vassilev       LE.setPrompt("clang-repl> ");
26192f9852fSVassil Vassilev     }
262f01d45c3SVassil Vassilev   }
26392f9852fSVassil Vassilev 
26492f9852fSVassil Vassilev   // Our error handler depends on the Diagnostics object, which we're
26592f9852fSVassil Vassilev   // potentially about to delete. Uninstall the handler now so that any
26692f9852fSVassil Vassilev   // later errors use the default handling behavior instead.
26792f9852fSVassil Vassilev   llvm::remove_fatal_error_handler();
26892f9852fSVassil Vassilev 
2699caee577SJun Zhang   return checkDiagErrors(Interp->getCompilerInstance(), HasError);
27092f9852fSVassil Vassilev }
271