diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 58a820508da42..c9840820a37ee 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -1099,6 +1099,8 @@ class ASTContext : public RefCountedBase { bool isInSameModule(const Module *M1, const Module *M2); TranslationUnitDecl *getTranslationUnitDecl() const { + assert(TUDecl->getMostRecentDecl() == TUDecl && + "The active TU is not current one!"); return TUDecl->getMostRecentDecl(); } void addTranslationUnitDecl() { diff --git a/clang/include/clang/Frontend/MultiplexConsumer.h b/clang/include/clang/Frontend/MultiplexConsumer.h index 3a7670d7a51aa..b190750bb29fb 100644 --- a/clang/include/clang/Frontend/MultiplexConsumer.h +++ b/clang/include/clang/Frontend/MultiplexConsumer.h @@ -53,6 +53,7 @@ class MultiplexConsumer : public SemaConsumer { public: // Takes ownership of the pointers in C. MultiplexConsumer(std::vector> C); + MultiplexConsumer(std::unique_ptr C); ~MultiplexConsumer() override; // ASTConsumer @@ -80,7 +81,7 @@ class MultiplexConsumer : public SemaConsumer { void InitializeSema(Sema &S) override; void ForgetSema() override; -private: +protected: std::vector> Consumers; // Owns these. std::unique_ptr MutationListener; std::unique_ptr DeserializationListener; diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h index 1234608bb5864..1763afd240a2c 100644 --- a/clang/include/clang/Interpreter/Interpreter.h +++ b/clang/include/clang/Interpreter/Interpreter.h @@ -14,11 +14,9 @@ #ifndef LLVM_CLANG_INTERPRETER_INTERPRETER_H #define LLVM_CLANG_INTERPRETER_INTERPRETER_H -#include "clang/AST/Decl.h" #include "clang/AST/GlobalDecl.h" #include "clang/Interpreter/PartialTranslationUnit.h" #include "clang/Interpreter/Value.h" -#include "clang/Sema/Ownership.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ExecutionEngine/JITSymbol.h" @@ -38,8 +36,12 @@ class ThreadSafeContext; namespace clang { class CompilerInstance; +class CodeGenerator; +class CXXRecordDecl; +class Decl; class IncrementalExecutor; class IncrementalParser; +class LookupResult; /// Create a pre-configured \c CompilerInstance for incremental processing. class IncrementalCompilerBuilder { @@ -77,26 +79,29 @@ class IncrementalCompilerBuilder { llvm::StringRef CudaSDKPath; }; -/// Generate glue code between the Interpreter's built-in runtime and user code. -class RuntimeInterfaceBuilder { -public: - virtual ~RuntimeInterfaceBuilder() = default; - - using TransformExprFunction = ExprResult(RuntimeInterfaceBuilder *Builder, - Expr *, ArrayRef); - virtual TransformExprFunction *getPrintValueTransformer() = 0; -}; +class IncrementalAction; +class InProcessPrintingASTConsumer; /// Provides top-level interfaces for incremental compilation and execution. class Interpreter { + friend class Value; + friend InProcessPrintingASTConsumer; + std::unique_ptr TSCtx; + /// Long-lived, incremental parsing action. + std::unique_ptr Act; std::unique_ptr IncrParser; std::unique_ptr IncrExecutor; - std::unique_ptr RuntimeIB; // An optional parser for CUDA offloading std::unique_ptr DeviceParser; + std::unique_ptr JITBuilder; + + /// List containing every information about every incrementally parsed piece + /// of code. + std::list PTUs; + unsigned InitPTUSize = 0; // This member holds the last result of the value printing. It's a class @@ -104,15 +109,26 @@ class Interpreter { // printing happens, it's in an invalid state. Value LastValue; - // Add a call to an Expr to report its result. We query the function from - // RuntimeInterfaceBuilder once and store it as a function pointer to avoid - // frequent virtual function calls. - RuntimeInterfaceBuilder::TransformExprFunction *AddPrintValueCall = nullptr; + // The cached declaration of std::string used as a return type for the built + // trampoline. This is done in C++ to simplify the memory management for + // user-defined printing functions. + Decl *StdString = nullptr; + + // A cache for the compiled destructors used to for de-allocation of managed + // clang::Values. + llvm::DenseMap Dtors; + + std::array ValuePrintingInfo = {0}; + + /// When CodeGen is created the first llvm::Module gets cached in many places + /// and we must keep it alive. + std::unique_ptr CachedInCodeGenModule; protected: // Derived classes can use an extended interface of the Interpreter. Interpreter(std::unique_ptr CI, llvm::Error &Err, - std::unique_ptr JITBuilder = nullptr); + std::unique_ptr JITBuilder = nullptr, + std::unique_ptr Consumer = nullptr); // Create the internal IncrementalExecutor, or re-create it after calling // ResetExecutor(). @@ -122,15 +138,8 @@ class Interpreter { // JIT engine. In particular, it doesn't run cleanup or destructors. void ResetExecutor(); - // Lazily construct the RuntimeInterfaceBuilder. The provided instance will be - // used for the entire lifetime of the interpreter. The default implementation - // targets the in-process __clang_Interpreter runtime. Override this to use a - // custom runtime. - virtual std::unique_ptr FindRuntimeInterface(); - public: virtual ~Interpreter(); - static llvm::Expected> create(std::unique_ptr CI); static llvm::Expected> @@ -145,7 +154,6 @@ class Interpreter { llvm::Expected Parse(llvm::StringRef Code); llvm::Error Execute(PartialTranslationUnit &T); llvm::Error ParseAndExecute(llvm::StringRef Code, Value *V = nullptr); - llvm::Expected CompileDtorCall(CXXRecordDecl *CXXRD); /// Undo N previous incremental inputs. llvm::Error Undo(unsigned N = 1); @@ -167,23 +175,30 @@ class Interpreter { llvm::Expected getSymbolAddressFromLinkerName(llvm::StringRef LinkerName) const; - enum InterfaceKind { NoAlloc, WithAlloc, CopyArray, NewTag }; - - const llvm::SmallVectorImpl &getValuePrintingInfo() const { - return ValuePrintingInfo; - } - - Expr *SynthesizeExpr(Expr *E); + std::unique_ptr GenModule(); private: size_t getEffectivePTUSize() const; void markUserCodeStart(); - llvm::DenseMap Dtors; + /// @} + /// @name Value and pretty printing support + /// @{ - llvm::SmallVector ValuePrintingInfo; + std::string ValueDataToString(const Value &V); + std::string ValueTypeToString(const Value &V) const; + std::string CallUserSpecifiedPrinter(LookupResult &R, const Value &V); - std::unique_ptr JITBuilder; + llvm::Expected convertExprToValue(Expr *E); + + // When we deallocate clang::Value we need to run the destructor of the type. + // This function forces emission of the needed dtor. + llvm::Expected CompileDtorCall(CXXRecordDecl *CXXRD); + + /// @} + /// @name Code generation + /// @{ + CodeGenerator *getCodeGen() const; }; } // namespace clang diff --git a/clang/include/clang/Interpreter/Value.h b/clang/include/clang/Interpreter/Value.h index d70e8f8719026..664f9e9008603 100644 --- a/clang/include/clang/Interpreter/Value.h +++ b/clang/include/clang/Interpreter/Value.h @@ -49,9 +49,10 @@ class raw_ostream; namespace clang { class ASTContext; -class Interpreter; class QualType; +class Interpreter; + #if defined(_WIN32) // REPL_EXTERNAL_VISIBILITY are symbols that we need to be able to locate // at runtime. On Windows, this requires them to be exported from any of the @@ -118,9 +119,9 @@ class REPL_EXTERNAL_VISIBILITY Value { ~Value(); void printType(llvm::raw_ostream &Out) const; - void printData(llvm::raw_ostream &Out) const; - void print(llvm::raw_ostream &Out) const; - void dump() const; + void printData(llvm::raw_ostream &Out); + void print(llvm::raw_ostream &Out); + void dump(); void clear(); ASTContext &getASTContext(); @@ -138,6 +139,7 @@ class REPL_EXTERNAL_VISIBILITY Value { void setOpaqueType(void *Ty) { OpaqueType = Ty; } void *getPtr() const; + void **getPtrAddress() const; void setPtr(void *Ptr) { Data.m_Ptr = Ptr; } #define X(type, name) \ @@ -176,7 +178,7 @@ class REPL_EXTERNAL_VISIBILITY Value { template struct convertFwd { static T cast(const Value &V) { if (V.isPointerOrObjectType()) - return (T)(uintptr_t)V.as(); + return *(T*)(uintptr_t)V.as(); if (!V.isValid() || V.isVoid()) { return T(); } @@ -204,6 +206,5 @@ template <> inline void *Value::as() const { return Data.m_Ptr; return (void *)as(); } - } // namespace clang #endif diff --git a/clang/lib/Frontend/MultiplexConsumer.cpp b/clang/lib/Frontend/MultiplexConsumer.cpp index 2158d176d1892..3fd3c9bd69037 100644 --- a/clang/lib/Frontend/MultiplexConsumer.cpp +++ b/clang/lib/Frontend/MultiplexConsumer.cpp @@ -298,6 +298,13 @@ MultiplexConsumer::MultiplexConsumer( } } +MultiplexConsumer::MultiplexConsumer(std::unique_ptr C) + : MultiplexConsumer([](std::unique_ptr Consumer) { + std::vector> Consumers; + Consumers.push_back(std::move(Consumer)); + return Consumers; + }(std::move(C))) {} + MultiplexConsumer::~MultiplexConsumer() {} void MultiplexConsumer::Initialize(ASTContext &Context) { diff --git a/clang/lib/Headers/CMakeLists.txt b/clang/lib/Headers/CMakeLists.txt index 5a62538792f30..82d07fc77f143 100644 --- a/clang/lib/Headers/CMakeLists.txt +++ b/clang/lib/Headers/CMakeLists.txt @@ -36,6 +36,7 @@ set(core_files tgmath.h unwind.h varargs.h + __clang_interpreter_runtime_printvalue.h ) set(arm_common_files diff --git a/clang/lib/Headers/__clang_interpreter_runtime_printvalue.h b/clang/lib/Headers/__clang_interpreter_runtime_printvalue.h new file mode 100644 index 0000000000000..38c20a5390387 --- /dev/null +++ b/clang/lib/Headers/__clang_interpreter_runtime_printvalue.h @@ -0,0 +1,269 @@ +//===--- __clang_interpreter_runtime_printvalue.h ---*- C++ +//-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines runtime functions used to print STL components in +// clang-repl. They are very heavy so we should only include it once and on +// demand. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_INTERPRETER_RUNTIME_PRINT_VALUE_H +#define LLVM_CLANG_INTERPRETER_RUNTIME_PRINT_VALUE_H + +#if !defined(__CLANG_REPL__) +#error "This file should only be included by clang-repl!" +#endif + +namespace caas { +namespace runtime {} +} // namespace caas +using namespace caas::runtime; + +#include +#include +#include +#include +#include + +// FIXME: We should include it somewhere instead of duplicating it... +#if __has_attribute(visibility) && \ + (!(defined(_WIN32) || defined(__CYGWIN__)) || \ + (defined(__MINGW32__) && defined(__clang__))) +#if defined(LLVM_BUILD_LLVM_DYLIB) || defined(LLVM_BUILD_SHARED_LIBS) +#define __REPL_EXTERNAL_VISIBILITY __attribute__((visibility("default"))) +#else +#define __REPL_EXTERNAL_VISIBILITY +#endif +#else +#if defined(_WIN32) +#define __REPL_EXTERNAL_VISIBILITY __declspec(dllexport) +#endif +#endif + +// Fallback. +template ::value>::type * = nullptr> +inline std::string PrintValueRuntime(const T &) { + return "{not representable}"; +} + +// Forward declare the pre-compiled printing functions. +#ifndef __DECL_PRINT_VALUE_RUNTIME +#define __DECL_PRINT_VALUE_RUNTIME(type) \ + __REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const type *__Ptr) +__DECL_PRINT_VALUE_RUNTIME(void); // +__DECL_PRINT_VALUE_RUNTIME(void *); // +__DECL_PRINT_VALUE_RUNTIME(char *const); // +__DECL_PRINT_VALUE_RUNTIME(char *); // +// __DECL_PRINT_VALUE_RUNTIME(bool); +// __DECL_PRINT_VALUE_RUNTIME(char); +// __DECL_PRINT_VALUE_RUNTIME(signed char); +// __DECL_PRINT_VALUE_RUNTIME(short); +// __DECL_PRINT_VALUE_RUNTIME(unsigned short); +// __DECL_PRINT_VALUE_RUNTIME(int); +// __DECL_PRINT_VALUE_RUNTIME(unsigned int); +// __DECL_PRINT_VALUE_RUNTIME(long); +// __DECL_PRINT_VALUE_RUNTIME(unsigned long); +// __DECL_PRINT_VALUE_RUNTIME(long long); +// __DECL_PRINT_VALUE_RUNTIME(unsigned long long); +// __DECL_PRINT_VALUE_RUNTIME(float); +// __DECL_PRINT_VALUE_RUNTIME(double); +// __DECL_PRINT_VALUE_RUNTIME(long double); +#endif + +namespace __repl_runtime_detail { + +// Custom void_t implementation for C++11 compatibility +template struct __repl_void_impl { + typedef void type; +}; + +// Helper to deduce the type of the expression 'std::begin(std::declval())' +template +using __repl_begin_result = decltype(std::begin(std::declval())); + +// Helper to deduce the type of the expression 'std::end(std::declval())' +template +using __repl_end_result = decltype(std::end(std::declval())); + +// Type trait to check if a type is iterable +template +struct __is_iterable : std::false_type {}; + +template +struct __is_iterable, + __repl_end_result>::type> + : std::true_type {}; + +// Type trait to check if a type is std::pair +template struct __is_pair : std::false_type {}; + +template +struct __is_pair> : std::true_type {}; + +// Type trait to check if a type is std::map (or any associative container with +// mapped_type) +template struct __is_map : std::false_type {}; + +template +struct __is_map::type> + : std::true_type {}; + +// The type of the elements is std::pair, and the container is a map like type. +template < + typename Container, typename Elt, + typename std::enable_if<__is_pair::value && __is_map::value, + bool>::type = true> +std::string __PrintCollectionElt(const Elt &Val) { + return PrintValueRuntime(&Val.first) + " => " + + PrintValueRuntime(&Val.second); +} + +// The type of the elements is std::pair, and the container isn't a map-like +// type. +template ::value && + !__is_map::value, + bool>::type = true> +std::string __PrintCollectionElt(const Elt &Val) { + return TuplePairPrintValue(&Val); +} + +template ::value, bool>::type = true> +std::string __PrintCollectionElt(const Elt &Val) { + return PrintValueRuntime(&Val); +} + +template (), + std::size_t TupleSize = std::tuple_size()> +struct __TupleLikePrinter { + static std::string print(const Tuple *T) { + constexpr std::size_t EltNum = TupleSize - N; + std::string Str; + // Not the first element. + if (EltNum != 0) + Str += ", "; + Str += PrintValueRuntime(&std::get(*T)); + // If N+1 is not smaller than the size of the tuple, + // reroute the call to the printing function to the + // no-op specialisation to stop recursion. + constexpr std::size_t Nm1 = N - 1; + Str += __TupleLikePrinter::print((const Tuple *)T); + return Str; + } +}; + +template +struct __TupleLikePrinter { + static std::string print(const Tuple *T) { return ""; } +}; + +template inline std::string TuplePairPrintValue(const T *Val) { + std::string Str("{ "); + Str += __TupleLikePrinter::print(Val); + Str += " }"; + return Str; +} + +struct __StdVectorBool { + bool Value; + __StdVectorBool(bool V) : Value(V) {} +}; +template +std::string __PrintCollectionElt(const __StdVectorBool &Val) { + return PrintValueRuntime(&Val.Value); +} + +} // namespace __repl_runtime_detail + +template ::value, + bool>::type = true> +inline std::string PrintValueRuntime(const Container *C) { + std::string Str("{ "); + + for (auto Beg = C->begin(), End = C->end(); Beg != End; Beg++) { + if (Beg != C->begin()) + Str += ", "; + Str += __repl_runtime_detail::__PrintCollectionElt(*Beg); + } + Str += " }"; + return Str; +} + +template +inline std::string PrintValueRuntime(const T (*Obj)[N]) { + if (N == 0) + return "{}"; + + std::string Str = "{ "; + for (size_t Idx = 0; Idx < N; ++Idx) { + Str += PrintValueRuntime(*Obj + Idx); + if (Idx < N - 1) + Str += ", "; + } + return Str + " }"; +} + +template inline std::string PrintValueRuntime(const char (*Obj)[N]) { + const auto *Str = reinterpret_cast(Obj); + return PrintValueRuntime(&Str); +} + +// std::vector +inline std::string PrintValueRuntime(const std::vector *Val) { + // Try our best to fix std::vector without too much of templated code. + std::vector<__repl_runtime_detail::__StdVectorBool> ValFixed(Val->begin(), + Val->end()); + return PrintValueRuntime(&ValFixed); +} + +// tuple +template +inline std::string PrintValueRuntime(const std::tuple *Val) { + using T = std::tuple; + return __repl_runtime_detail::TuplePairPrintValue(Val); +} + +// pair +template +inline std::string PrintValueRuntime(const std::pair *Val) { + using T = std::pair; + return __repl_runtime_detail::TuplePairPrintValue(Val); +} + +// unique_ptr +template +inline std::string PrintValueRuntime(const std::unique_ptr *Val) { + auto Ptr = Val->get(); + return "std::unique_ptr -> " + PrintValueRuntime((const void **)&Ptr); +} + +// shared_ptr +template +inline std::string PrintValueRuntime(const std::shared_ptr *Val) { + auto Ptr = Val->get(); + return "std::shared_ptr -> " + PrintValueRuntime((const void **)&Ptr); +} + +// weak_ptr +template +inline std::string PrintValueRuntime(const std::weak_ptr *Val) { + auto Ptr = Val->lock().get(); + return "std::weak_ptr -> " + PrintValueRuntime((const void **)&Ptr); +} + +// string +template +inline std::string PrintValueRuntime(const std::basic_string *Val) { + const char *Chars = Val->c_str(); + return PrintValueRuntime((const char **)&Chars); +} +#endif diff --git a/clang/lib/Interpreter/CMakeLists.txt b/clang/lib/Interpreter/CMakeLists.txt index 6a069659ebb8d..8d95b78bd10be 100644 --- a/clang/lib/Interpreter/CMakeLists.txt +++ b/clang/lib/Interpreter/CMakeLists.txt @@ -24,6 +24,7 @@ add_clang_library(clangInterpreter Interpreter.cpp InterpreterUtils.cpp Value.cpp + InterpreterValuePrinter.cpp ${WASM_SRC} PARTIAL_SOURCES_INTENDED diff --git a/clang/lib/Interpreter/DeviceOffload.cpp b/clang/lib/Interpreter/DeviceOffload.cpp index 07c9e3005e5fd..4bf9ed2809689 100644 --- a/clang/lib/Interpreter/DeviceOffload.cpp +++ b/clang/lib/Interpreter/DeviceOffload.cpp @@ -15,6 +15,7 @@ #include "clang/Basic/TargetOptions.h" #include "clang/CodeGen/ModuleBuilder.h" #include "clang/Frontend/CompilerInstance.h" +#include "clang/Interpreter/PartialTranslationUnit.h" #include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/Module.h" @@ -24,11 +25,10 @@ namespace clang { IncrementalCUDADeviceParser::IncrementalCUDADeviceParser( - Interpreter &Interp, std::unique_ptr Instance, - IncrementalParser &HostParser, llvm::LLVMContext &LLVMCtx, + std::unique_ptr Instance, IncrementalParser &HostParser, llvm::IntrusiveRefCntPtr FS, - llvm::Error &Err) - : IncrementalParser(Interp, std::move(Instance), LLVMCtx, Err), + llvm::Error &Err, const std::list &PTUs) + : IncrementalParser(std::move(Instance), Err), PTUs(PTUs), HostParser(HostParser), VFS(FS) { if (Err) return; @@ -41,7 +41,7 @@ IncrementalCUDADeviceParser::IncrementalCUDADeviceParser( } } -llvm::Expected +llvm::Expected IncrementalCUDADeviceParser::Parse(llvm::StringRef Input) { auto PTU = IncrementalParser::Parse(Input); if (!PTU) diff --git a/clang/lib/Interpreter/DeviceOffload.h b/clang/lib/Interpreter/DeviceOffload.h index ce4f218c94c79..b84870474841a 100644 --- a/clang/lib/Interpreter/DeviceOffload.h +++ b/clang/lib/Interpreter/DeviceOffload.h @@ -18,19 +18,19 @@ #include "llvm/Support/VirtualFileSystem.h" namespace clang { - +struct PartialTranslationUnit; class IncrementalCUDADeviceParser : public IncrementalParser { + const std::list &PTUs; + public: IncrementalCUDADeviceParser( - Interpreter &Interp, std::unique_ptr Instance, - IncrementalParser &HostParser, llvm::LLVMContext &LLVMCtx, + std::unique_ptr Instance, IncrementalParser &HostParser, llvm::IntrusiveRefCntPtr VFS, - llvm::Error &Err); + llvm::Error &Err, const std::list &PTUs); - llvm::Expected - Parse(llvm::StringRef Input) override; + llvm::Expected Parse(llvm::StringRef Input) override; - // Generate PTX for the last PTU + // Generate PTX for the last PTU. llvm::Expected GeneratePTX(); // Generate fatbinary contents in memory diff --git a/clang/lib/Interpreter/IncrementalExecutor.h b/clang/lib/Interpreter/IncrementalExecutor.h index 7954cde36588b..4e3afb7de26e0 100644 --- a/clang/lib/Interpreter/IncrementalExecutor.h +++ b/clang/lib/Interpreter/IncrementalExecutor.h @@ -32,9 +32,10 @@ class ThreadSafeContext; namespace clang { -struct PartialTranslationUnit; class TargetInfo; +struct PartialTranslationUnit; + class IncrementalExecutor { using CtorDtorIterator = llvm::orc::CtorDtorIterator; std::unique_ptr Jit; @@ -65,7 +66,6 @@ class IncrementalExecutor { static llvm::Expected> createDefaultJITBuilder(llvm::orc::JITTargetMachineBuilder JTMB); }; - } // end namespace clang #endif // LLVM_CLANG_LIB_INTERPRETER_INCREMENTALEXECUTOR_H diff --git a/clang/lib/Interpreter/IncrementalParser.cpp b/clang/lib/Interpreter/IncrementalParser.cpp index b7c809c45098c..615f61e9aec1b 100644 --- a/clang/lib/Interpreter/IncrementalParser.cpp +++ b/clang/lib/Interpreter/IncrementalParser.cpp @@ -13,233 +13,33 @@ #include "IncrementalParser.h" #include "clang/AST/DeclContextInternals.h" -#include "clang/CodeGen/BackendUtil.h" -#include "clang/CodeGen/CodeGenAction.h" -#include "clang/CodeGen/ModuleBuilder.h" #include "clang/Frontend/CompilerInstance.h" -#include "clang/Frontend/FrontendAction.h" -#include "clang/FrontendTool/Utils.h" -#include "clang/Interpreter/Interpreter.h" +#include "clang/Interpreter/PartialTranslationUnit.h" #include "clang/Parse/Parser.h" #include "clang/Sema/Sema.h" -#include "llvm/Option/ArgList.h" #include "llvm/Support/CrashRecoveryContext.h" #include "llvm/Support/Error.h" -#include "llvm/Support/Timer.h" #include namespace clang { -class IncrementalASTConsumer final : public ASTConsumer { - Interpreter &Interp; - std::unique_ptr Consumer; - -public: - IncrementalASTConsumer(Interpreter &InterpRef, std::unique_ptr C) - : Interp(InterpRef), Consumer(std::move(C)) {} - - bool HandleTopLevelDecl(DeclGroupRef DGR) override final { - if (DGR.isNull()) - return true; - if (!Consumer) - return true; - - for (Decl *D : DGR) - if (auto *TSD = llvm::dyn_cast(D); - TSD && TSD->isSemiMissing()) - TSD->setStmt(Interp.SynthesizeExpr(cast(TSD->getStmt()))); - - return Consumer->HandleTopLevelDecl(DGR); - } - void HandleTranslationUnit(ASTContext &Ctx) override final { - Consumer->HandleTranslationUnit(Ctx); - } - void HandleInlineFunctionDefinition(FunctionDecl *D) override final { - Consumer->HandleInlineFunctionDefinition(D); - } - void HandleInterestingDecl(DeclGroupRef D) override final { - Consumer->HandleInterestingDecl(D); - } - void HandleTagDeclDefinition(TagDecl *D) override final { - Consumer->HandleTagDeclDefinition(D); - } - void HandleTagDeclRequiredDefinition(const TagDecl *D) override final { - Consumer->HandleTagDeclRequiredDefinition(D); - } - void HandleCXXImplicitFunctionInstantiation(FunctionDecl *D) override final { - Consumer->HandleCXXImplicitFunctionInstantiation(D); - } - void HandleTopLevelDeclInObjCContainer(DeclGroupRef D) override final { - Consumer->HandleTopLevelDeclInObjCContainer(D); - } - void HandleImplicitImportDecl(ImportDecl *D) override final { - Consumer->HandleImplicitImportDecl(D); - } - void CompleteTentativeDefinition(VarDecl *D) override final { - Consumer->CompleteTentativeDefinition(D); - } - void CompleteExternalDeclaration(DeclaratorDecl *D) override final { - Consumer->CompleteExternalDeclaration(D); - } - void AssignInheritanceModel(CXXRecordDecl *RD) override final { - Consumer->AssignInheritanceModel(RD); - } - void HandleCXXStaticMemberVarInstantiation(VarDecl *D) override final { - Consumer->HandleCXXStaticMemberVarInstantiation(D); - } - void HandleVTable(CXXRecordDecl *RD) override final { - Consumer->HandleVTable(RD); - } - ASTMutationListener *GetASTMutationListener() override final { - return Consumer->GetASTMutationListener(); - } - ASTDeserializationListener *GetASTDeserializationListener() override final { - return Consumer->GetASTDeserializationListener(); - } - void PrintStats() override final { Consumer->PrintStats(); } - bool shouldSkipFunctionBody(Decl *D) override final { - return Consumer->shouldSkipFunctionBody(D); - } - static bool classof(const clang::ASTConsumer *) { return true; } -}; - -/// A custom action enabling the incremental processing functionality. -/// -/// The usual \p FrontendAction expects one call to ExecuteAction and once it -/// sees a call to \p EndSourceFile it deletes some of the important objects -/// such as \p Preprocessor and \p Sema assuming no further input will come. -/// -/// \p IncrementalAction ensures it keep its underlying action's objects alive -/// as long as the \p IncrementalParser needs them. -/// -class IncrementalAction : public WrapperFrontendAction { -private: - bool IsTerminating = false; - -public: - IncrementalAction(CompilerInstance &CI, llvm::LLVMContext &LLVMCtx, - llvm::Error &Err) - : WrapperFrontendAction([&]() { - llvm::ErrorAsOutParameter EAO(&Err); - std::unique_ptr Act; - switch (CI.getFrontendOpts().ProgramAction) { - default: - Err = llvm::createStringError( - std::errc::state_not_recoverable, - "Driver initialization failed. " - "Incremental mode for action %d is not supported", - CI.getFrontendOpts().ProgramAction); - return Act; - case frontend::ASTDump: - [[fallthrough]]; - case frontend::ASTPrint: - [[fallthrough]]; - case frontend::ParseSyntaxOnly: - Act = CreateFrontendAction(CI); - break; - case frontend::PluginAction: - [[fallthrough]]; - case frontend::EmitAssembly: - [[fallthrough]]; - case frontend::EmitBC: - [[fallthrough]]; - case frontend::EmitObj: - [[fallthrough]]; - case frontend::PrintPreprocessedInput: - [[fallthrough]]; - case frontend::EmitLLVMOnly: - Act.reset(new EmitLLVMOnlyAction(&LLVMCtx)); - break; - } - return Act; - }()) {} - FrontendAction *getWrapped() const { return WrappedAction.get(); } - TranslationUnitKind getTranslationUnitKind() override { - return TU_Incremental; - } - - void ExecuteAction() override { - CompilerInstance &CI = getCompilerInstance(); - assert(CI.hasPreprocessor() && "No PP!"); - - // Use a code completion consumer? - CodeCompleteConsumer *CompletionConsumer = nullptr; - if (CI.hasCodeCompletionConsumer()) - CompletionConsumer = &CI.getCodeCompletionConsumer(); - - Preprocessor &PP = CI.getPreprocessor(); - PP.EnterMainSourceFile(); - - if (!CI.hasSema()) - CI.createSema(getTranslationUnitKind(), CompletionConsumer); - } - - // Do not terminate after processing the input. This allows us to keep various - // clang objects alive and to incrementally grow the current TU. - void EndSourceFile() override { - // The WrappedAction can be nullptr if we issued an error in the ctor. - if (IsTerminating && getWrapped()) - WrapperFrontendAction::EndSourceFile(); - } - - void FinalizeAction() { - assert(!IsTerminating && "Already finalized!"); - IsTerminating = true; - EndSourceFile(); - } -}; - -CodeGenerator *IncrementalParser::getCodeGen() const { - FrontendAction *WrappedAct = Act->getWrapped(); - if (!WrappedAct->hasIRSupport()) - return nullptr; - return static_cast(WrappedAct)->getCodeGenerator(); -} - IncrementalParser::IncrementalParser() {} -IncrementalParser::IncrementalParser(Interpreter &Interp, - std::unique_ptr Instance, - llvm::LLVMContext &LLVMCtx, +IncrementalParser::IncrementalParser(std::unique_ptr Instance, llvm::Error &Err) : CI(std::move(Instance)) { llvm::ErrorAsOutParameter EAO(&Err); - Act = std::make_unique(*CI, LLVMCtx, Err); - if (Err) - return; - CI->ExecuteAction(*Act); - if (getCodeGen()) - CachedInCodeGenModule = GenModule(); - - std::unique_ptr IncrConsumer = - std::make_unique(Interp, CI->takeASTConsumer()); - CI->setASTConsumer(std::move(IncrConsumer)); Consumer = &CI->getASTConsumer(); P.reset( new Parser(CI->getPreprocessor(), CI->getSema(), /*SkipBodies=*/false)); P->Initialize(); - - // An initial PTU is needed as CUDA includes some headers automatically - auto PTU = ParseOrWrapTopLevelDecl(); - if (auto E = PTU.takeError()) { - consumeError(std::move(E)); // FIXME - return; // PTU.takeError(); - } - - if (getCodeGen()) { - PTU->TheModule = GenModule(); - assert(PTU->TheModule && "Failed to create initial PTU"); - } } -IncrementalParser::~IncrementalParser() { - P.reset(); - Act->FinalizeAction(); -} +IncrementalParser::~IncrementalParser() { P.reset(); } -llvm::Expected +llvm::Expected IncrementalParser::ParseOrWrapTopLevelDecl() { // Recover resources if we crash before exiting this method. Sema &S = CI->getSema(); @@ -247,12 +47,9 @@ IncrementalParser::ParseOrWrapTopLevelDecl() { Sema::GlobalEagerInstantiationScope GlobalInstantiations(S, /*Enabled=*/true); Sema::LocalEagerInstantiationScope LocalInstantiations(S); - PTUs.emplace_back(PartialTranslationUnit()); - PartialTranslationUnit &LastPTU = PTUs.back(); // Add a new PTU. ASTContext &C = S.getASTContext(); C.addTranslationUnitDecl(); - LastPTU.TUPart = C.getTranslationUnitDecl(); // Skip previous eof due to last incremental input. if (P->getCurToken().is(tok::annot_repl_input_end)) { @@ -278,9 +75,7 @@ IncrementalParser::ParseOrWrapTopLevelDecl() { DiagnosticsEngine &Diags = getCI()->getDiagnostics(); if (Diags.hasErrorOccurred()) { - PartialTranslationUnit MostRecentPTU = {C.getTranslationUnitDecl(), - nullptr}; - CleanUpPTU(MostRecentPTU); + CleanUpPTU(C.getTranslationUnitDecl()); Diags.Reset(/*soft=*/true); Diags.getClient()->clear(); @@ -299,10 +94,10 @@ IncrementalParser::ParseOrWrapTopLevelDecl() { Consumer->HandleTranslationUnit(C); - return LastPTU; + return C.getTranslationUnitDecl(); } -llvm::Expected +llvm::Expected IncrementalParser::Parse(llvm::StringRef input) { Preprocessor &PP = CI->getPreprocessor(); assert(PP.isIncrementalProcessingEnabled() && "Not in incremental mode!?"); @@ -356,37 +151,10 @@ IncrementalParser::Parse(llvm::StringRef input) { "Lexer must be EOF when starting incremental parse!"); } - if (std::unique_ptr M = GenModule()) - PTU->TheModule = std::move(M); - return PTU; } -std::unique_ptr IncrementalParser::GenModule() { - static unsigned ID = 0; - if (CodeGenerator *CG = getCodeGen()) { - // Clang's CodeGen is designed to work with a single llvm::Module. In many - // cases for convenience various CodeGen parts have a reference to the - // llvm::Module (TheModule or Module) which does not change when a new - // module is pushed. However, the execution engine wants to take ownership - // of the module which does not map well to CodeGen's design. To work this - // around we created an empty module to make CodeGen happy. We should make - // sure it always stays empty. - assert((!CachedInCodeGenModule || - (CachedInCodeGenModule->empty() && - CachedInCodeGenModule->global_empty() && - CachedInCodeGenModule->alias_empty() && - CachedInCodeGenModule->ifunc_empty())) && - "CodeGen wrote to a readonly module"); - std::unique_ptr M(CG->ReleaseModule()); - CG->StartModule("incr_module_" + std::to_string(ID++), M->getContext()); - return M; - } - return nullptr; -} - -void IncrementalParser::CleanUpPTU(PartialTranslationUnit &PTU) { - TranslationUnitDecl *MostRecentTU = PTU.TUPart; +void IncrementalParser::CleanUpPTU(TranslationUnitDecl *MostRecentTU) { if (StoredDeclsMap *Map = MostRecentTU->getPrimaryContext()->getLookupPtr()) { for (auto &&[Key, List] : *Map) { DeclContextLookupResult R = List.getLookupResult(); @@ -419,9 +187,4 @@ void IncrementalParser::CleanUpPTU(PartialTranslationUnit &PTU) { } } -llvm::StringRef IncrementalParser::GetMangledName(GlobalDecl GD) const { - CodeGenerator *CG = getCodeGen(); - assert(CG); - return CG->GetMangledName(GD); -} } // end namespace clang diff --git a/clang/lib/Interpreter/IncrementalParser.h b/clang/lib/Interpreter/IncrementalParser.h index f63bce50acd3b..4ce361143b581 100644 --- a/clang/lib/Interpreter/IncrementalParser.h +++ b/clang/lib/Interpreter/IncrementalParser.h @@ -13,35 +13,24 @@ #ifndef LLVM_CLANG_LIB_INTERPRETER_INCREMENTALPARSER_H #define LLVM_CLANG_LIB_INTERPRETER_INCREMENTALPARSER_H -#include "clang/AST/GlobalDecl.h" -#include "clang/Interpreter/PartialTranslationUnit.h" - -#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Error.h" #include #include -namespace llvm { -class LLVMContext; -class Module; -} // namespace llvm namespace clang { class ASTConsumer; class CodeGenerator; class CompilerInstance; -class IncrementalAction; -class Interpreter; class Parser; +class TranslationUnitDecl; + /// Provides support for incremental compilation. Keeps track of the state /// changes between the subsequent incremental input. /// class IncrementalParser { protected: - /// Long-lived, incremental parsing action. - std::unique_ptr Act; - /// Compiler instance performing the incremental compilation. std::unique_ptr CI; @@ -54,42 +43,24 @@ class IncrementalParser { /// Counts the number of direct user input lines that have been parsed. unsigned InputCount = 0; - /// List containing every information about every incrementally parsed piece - /// of code. - std::list PTUs; - - /// When CodeGen is created the first llvm::Module gets cached in many places - /// and we must keep it alive. - std::unique_ptr CachedInCodeGenModule; - IncrementalParser(); public: - IncrementalParser(Interpreter &Interp, - std::unique_ptr Instance, - llvm::LLVMContext &LLVMCtx, llvm::Error &Err); + IncrementalParser(std::unique_ptr Instance, + llvm::Error &Err); virtual ~IncrementalParser(); CompilerInstance *getCI() { return CI.get(); } - CodeGenerator *getCodeGen() const; /// Parses incremental input by creating an in-memory file. ///\returns a \c PartialTranslationUnit which holds information about the - /// \c TranslationUnitDecl and \c llvm::Module corresponding to the input. - virtual llvm::Expected Parse(llvm::StringRef Input); - - /// Uses the CodeGenModule mangled name cache and avoids recomputing. - ///\returns the mangled name of a \c GD. - llvm::StringRef GetMangledName(GlobalDecl GD) const; - - void CleanUpPTU(PartialTranslationUnit &PTU); - - std::list &getPTUs() { return PTUs; } + /// \c TranslationUnitDecl. + virtual llvm::Expected Parse(llvm::StringRef Input); - std::unique_ptr GenModule(); + void CleanUpPTU(TranslationUnitDecl *MostRecentTU); private: - llvm::Expected ParseOrWrapTopLevelDecl(); + llvm::Expected ParseOrWrapTopLevelDecl(); }; } // end namespace clang diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp index 7209a33272ef2..b4cf63f00e0be 100644 --- a/clang/lib/Interpreter/Interpreter.cpp +++ b/clang/lib/Interpreter/Interpreter.cpp @@ -19,6 +19,7 @@ #include "Wasm.h" #endif // __EMSCRIPTEN__ +#include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Mangle.h" #include "clang/AST/TypeVisitor.h" @@ -33,7 +34,10 @@ #include "clang/Driver/Options.h" #include "clang/Driver/Tool.h" #include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendAction.h" +#include "clang/Frontend/MultiplexConsumer.h" #include "clang/Frontend/TextDiagnosticBuffer.h" +#include "clang/FrontendTool/Utils.h" #include "clang/Interpreter/Interpreter.h" #include "clang/Interpreter/Value.h" #include "clang/Lex/PreprocessorOptions.h" @@ -50,7 +54,6 @@ #include using namespace clang; - // FIXME: Figure out how to unify with namespace init_convenience from // tools/clang-import-test/clang-import-test.cpp namespace { @@ -138,6 +141,8 @@ CreateCI(const llvm::opt::ArgStringList &Argv) { } // anonymous namespace +namespace clang { + llvm::Expected> IncrementalCompilerBuilder::create(std::string TT, std::vector &ClangArgv) { @@ -241,20 +246,177 @@ IncrementalCompilerBuilder::CreateCudaHost() { return IncrementalCompilerBuilder::createCuda(false); } +class InProcessPrintingASTConsumer final : public MultiplexConsumer { + Interpreter &Interp; + +public: + InProcessPrintingASTConsumer(std::unique_ptr C, Interpreter &I) + : MultiplexConsumer(std::move(C)), Interp(I) {} + bool HandleTopLevelDecl(DeclGroupRef DGR) override final { + if (DGR.isNull()) + return true; + // if (!Consumer) + // return true; + + for (Decl *D : DGR) + if (auto *TLSD = llvm::dyn_cast(D)) + if (TLSD && TLSD->isSemiMissing()) { + auto ExprOrErr = + Interp.convertExprToValue(cast(TLSD->getStmt())); + if (llvm::Error E = ExprOrErr.takeError()) { + llvm::logAllUnhandledErrors(std::move(E), llvm::errs(), + "Value printing failed: "); + return false; // abort parsing + } + TLSD->setStmt(*ExprOrErr); + } + + return MultiplexConsumer::HandleTopLevelDecl(DGR); + } +}; + +/// A custom action enabling the incremental processing functionality. +/// +/// The usual \p FrontendAction expects one call to ExecuteAction and once it +/// sees a call to \p EndSourceFile it deletes some of the important objects +/// such as \p Preprocessor and \p Sema assuming no further input will come. +/// +/// \p IncrementalAction ensures it keep its underlying action's objects alive +/// as long as the \p IncrementalParser needs them. +/// +class IncrementalAction : public WrapperFrontendAction { +private: + bool IsTerminating = false; + Interpreter &Interp; + std::unique_ptr Consumer; + +public: + IncrementalAction(CompilerInstance &CI, llvm::LLVMContext &LLVMCtx, + llvm::Error &Err, Interpreter &I, + std::unique_ptr Consumer = nullptr) + : WrapperFrontendAction([&]() { + llvm::ErrorAsOutParameter EAO(&Err); + std::unique_ptr Act; + switch (CI.getFrontendOpts().ProgramAction) { + default: + Err = llvm::createStringError( + std::errc::state_not_recoverable, + "Driver initialization failed. " + "Incremental mode for action %d is not supported", + CI.getFrontendOpts().ProgramAction); + return Act; + case frontend::ASTDump: + [[fallthrough]]; + case frontend::ASTPrint: + [[fallthrough]]; + case frontend::ParseSyntaxOnly: + Act = CreateFrontendAction(CI); + break; + case frontend::PluginAction: + [[fallthrough]]; + case frontend::EmitAssembly: + [[fallthrough]]; + case frontend::EmitBC: + [[fallthrough]]; + case frontend::EmitObj: + [[fallthrough]]; + case frontend::PrintPreprocessedInput: + [[fallthrough]]; + case frontend::EmitLLVMOnly: + Act.reset(new EmitLLVMOnlyAction(&LLVMCtx)); + break; + } + return Act; + }()), + Interp(I), Consumer(std::move(Consumer)) {} + FrontendAction *getWrapped() const { return WrappedAction.get(); } + TranslationUnitKind getTranslationUnitKind() override { + return TU_Incremental; + } + + std::unique_ptr CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) override { + std::unique_ptr C = + WrapperFrontendAction::CreateASTConsumer(CI, InFile); + + if (Consumer) { + std::vector> Cs; + Cs.push_back(std::move(Consumer)); + Cs.push_back(std::move(C)); + return std::make_unique(std::move(Cs)); + } + + return std::make_unique(std::move(C), Interp); + } + + void ExecuteAction() override { + CompilerInstance &CI = getCompilerInstance(); + assert(CI.hasPreprocessor() && "No PP!"); + + // Use a code completion consumer? + CodeCompleteConsumer *CompletionConsumer = nullptr; + if (CI.hasCodeCompletionConsumer()) + CompletionConsumer = &CI.getCodeCompletionConsumer(); + + Preprocessor &PP = CI.getPreprocessor(); + PP.EnterMainSourceFile(); + + if (!CI.hasSema()) + CI.createSema(getTranslationUnitKind(), CompletionConsumer); + } + + // Do not terminate after processing the input. This allows us to keep various + // clang objects alive and to incrementally grow the current TU. + void EndSourceFile() override { + // The WrappedAction can be nullptr if we issued an error in the ctor. + if (IsTerminating && getWrapped()) + WrapperFrontendAction::EndSourceFile(); + } + + void FinalizeAction() { + assert(!IsTerminating && "Already finalized!"); + IsTerminating = true; + EndSourceFile(); + } +}; + Interpreter::Interpreter(std::unique_ptr CI, llvm::Error &ErrOut, - std::unique_ptr JITBuilder) + std::unique_ptr JITBuilder, + std::unique_ptr Consumer) : JITBuilder(std::move(JITBuilder)) { llvm::ErrorAsOutParameter EAO(&ErrOut); auto LLVMCtx = std::make_unique(); TSCtx = std::make_unique(std::move(LLVMCtx)); - IncrParser = std::make_unique( - *this, std::move(CI), *TSCtx->getContext(), ErrOut); + + Act = std::make_unique(*CI, *TSCtx->getContext(), ErrOut, + *this, std::move(Consumer)); + if (ErrOut) + return; + CI->ExecuteAction(*Act); + + if (getCodeGen()) + CachedInCodeGenModule = GenModule(); + + IncrParser = std::make_unique(std::move(CI), ErrOut); + + // An initial PTU is needed as CUDA includes some headers automatically. + auto PTU = Parse(""); + if (auto E = PTU.takeError()) { + ErrOut = joinErrors(std::move(ErrOut), std::move(E)); + return; + } + + if (getCodeGen()) { + PTU->TheModule = GenModule(); + assert(PTU->TheModule && "Failed to create initial PTU"); + } + if (ErrOut) return; // Not all frontends support code-generation, e.g. ast-dump actions don't - if (IncrParser->getCodeGen()) { + if (getCodeGen()) { if (llvm::Error Err = CreateExecutor()) { ErrOut = joinErrors(std::move(ErrOut), std::move(Err)); return; @@ -262,7 +424,7 @@ Interpreter::Interpreter(std::unique_ptr CI, // Process the PTUs that came from initialization. For example -include will // give us a header that's processed at initialization of the preprocessor. - for (PartialTranslationUnit &PTU : IncrParser->getPTUs()) + for (PartialTranslationUnit &PTU : PTUs) if (llvm::Error Err = Execute(PTU)) { ErrOut = joinErrors(std::move(ErrOut), std::move(Err)); return; @@ -271,6 +433,8 @@ Interpreter::Interpreter(std::unique_ptr CI, } Interpreter::~Interpreter() { + Act->FinalizeAction(); + IncrParser.reset(); if (IncrExecutor) { if (llvm::Error Err = IncrExecutor->cleanUp()) llvm::report_fatal_error( @@ -320,7 +484,6 @@ Interpreter::create(std::unique_ptr CI) { return PTU.takeError(); Interp->markUserCodeStart(); - Interp->ValuePrintingInfo.resize(4); return std::move(Interp); } @@ -342,8 +505,8 @@ Interpreter::createWithCUDA(std::unique_ptr CI, llvm::Error Err = llvm::Error::success(); auto DeviceParser = std::make_unique( - **Interp, std::move(DCI), *(*Interp)->IncrParser.get(), - *(*Interp)->TSCtx->getContext(), IMVFS, Err); + std::move(DCI), *(*Interp)->IncrParser.get(), IMVFS, Err, + (*Interp)->PTUs); if (Err) return std::move(Err); @@ -379,22 +542,21 @@ const ASTContext &Interpreter::getASTContext() const { void Interpreter::markUserCodeStart() { assert(!InitPTUSize && "We only do this once"); - InitPTUSize = IncrParser->getPTUs().size(); + InitPTUSize = PTUs.size(); } size_t Interpreter::getEffectivePTUSize() const { - std::list &PTUs = IncrParser->getPTUs(); assert(PTUs.size() >= InitPTUSize && "empty PTU list?"); return PTUs.size() - InitPTUSize; } llvm::Expected Interpreter::Parse(llvm::StringRef Code) { - // If we have a device parser, parse it first. - // The generated code will be included in the host compilation + // If we have a device parser, parse it first. The generated code will be + // included in the host compilation if (DeviceParser) { - auto DevicePTU = DeviceParser->Parse(Code); - if (auto E = DevicePTU.takeError()) + llvm::Expected DeviceTU = DeviceParser->Parse(Code); + if (auto E = DeviceTU.takeError()) return std::move(E); } @@ -402,7 +564,19 @@ Interpreter::Parse(llvm::StringRef Code) { // printing could cause it. getCompilerInstance()->getDiagnostics().setSeverity( clang::diag::warn_unused_expr, diag::Severity::Ignored, SourceLocation()); - return IncrParser->Parse(Code); + + llvm::Expected TuOrErr = IncrParser->Parse(Code); + if (!TuOrErr) + return TuOrErr.takeError(); + + PTUs.emplace_back(PartialTranslationUnit()); + PartialTranslationUnit &LastPTU = PTUs.back(); + LastPTU.TUPart = *TuOrErr; + + if (std::unique_ptr M = GenModule()) + LastPTU.TheModule = std::move(M); + + return LastPTU; } static llvm::Expected @@ -420,7 +594,7 @@ llvm::Error Interpreter::CreateExecutor() { return llvm::make_error("Operation failed. " "Execution engine exists", std::error_code()); - if (!IncrParser->getCodeGen()) + if (!getCodeGen()) return llvm::make_error("Operation failed. " "No code generator available", std::error_code()); @@ -492,7 +666,7 @@ Interpreter::getSymbolAddress(GlobalDecl GD) const { return llvm::make_error("Operation failed. " "No execution engine", std::error_code()); - llvm::StringRef MangledName = IncrParser->GetMangledName(GD); + llvm::StringRef MangledName = getCodeGen()->GetMangledName(GD); return getSymbolAddress(MangledName); } @@ -518,7 +692,6 @@ Interpreter::getSymbolAddressFromLinkerName(llvm::StringRef Name) const { llvm::Error Interpreter::Undo(unsigned N) { - std::list &PTUs = IncrParser->getPTUs(); if (N > getEffectivePTUSize()) return llvm::make_error("Operation failed. " "Too many undos", @@ -529,7 +702,7 @@ llvm::Error Interpreter::Undo(unsigned N) { return Err; } - IncrParser->CleanUpPTU(PTUs.back()); + IncrParser->CleanUpPTU(PTUs.back().TUPart); PTUs.pop_back(); } return llvm::Error::success(); @@ -551,416 +724,33 @@ llvm::Error Interpreter::LoadDynamicLibrary(const char *name) { return llvm::Error::success(); } -llvm::Expected -Interpreter::CompileDtorCall(CXXRecordDecl *CXXRD) { - assert(CXXRD && "Cannot compile a destructor for a nullptr"); - if (auto Dtor = Dtors.find(CXXRD); Dtor != Dtors.end()) - return Dtor->getSecond(); - - if (CXXRD->hasIrrelevantDestructor()) - return llvm::orc::ExecutorAddr{}; - - CXXDestructorDecl *DtorRD = - getCompilerInstance()->getSema().LookupDestructor(CXXRD); - - llvm::StringRef Name = - IncrParser->GetMangledName(GlobalDecl(DtorRD, Dtor_Base)); - auto AddrOrErr = getSymbolAddress(Name); - if (!AddrOrErr) - return AddrOrErr.takeError(); - - Dtors[CXXRD] = *AddrOrErr; - return AddrOrErr; -} - -static constexpr llvm::StringRef MagicRuntimeInterface[] = { - "__clang_Interpreter_SetValueNoAlloc", - "__clang_Interpreter_SetValueWithAlloc", - "__clang_Interpreter_SetValueCopyArr", "__ci_newtag"}; - -static std::unique_ptr -createInProcessRuntimeInterfaceBuilder(Interpreter &Interp, ASTContext &Ctx, - Sema &S); - -std::unique_ptr Interpreter::FindRuntimeInterface() { - if (llvm::all_of(ValuePrintingInfo, [](Expr *E) { return E != nullptr; })) - return nullptr; - - Sema &S = getCompilerInstance()->getSema(); - ASTContext &Ctx = S.getASTContext(); - - auto LookupInterface = [&](Expr *&Interface, llvm::StringRef Name) { - LookupResult R(S, &Ctx.Idents.get(Name), SourceLocation(), - Sema::LookupOrdinaryName, - RedeclarationKind::ForVisibleRedeclaration); - S.LookupQualifiedName(R, Ctx.getTranslationUnitDecl()); - if (R.empty()) - return false; - - CXXScopeSpec CSS; - Interface = S.BuildDeclarationNameExpr(CSS, R, /*ADL=*/false).get(); - return true; - }; - - if (!LookupInterface(ValuePrintingInfo[NoAlloc], - MagicRuntimeInterface[NoAlloc])) - return nullptr; - if (Ctx.getLangOpts().CPlusPlus) { - if (!LookupInterface(ValuePrintingInfo[WithAlloc], - MagicRuntimeInterface[WithAlloc])) - return nullptr; - if (!LookupInterface(ValuePrintingInfo[CopyArray], - MagicRuntimeInterface[CopyArray])) - return nullptr; - if (!LookupInterface(ValuePrintingInfo[NewTag], - MagicRuntimeInterface[NewTag])) - return nullptr; +std::unique_ptr Interpreter::GenModule() { + static unsigned ID = 0; + if (CodeGenerator *CG = getCodeGen()) { + // Clang's CodeGen is designed to work with a single llvm::Module. In many + // cases for convenience various CodeGen parts have a reference to the + // llvm::Module (TheModule or Module) which does not change when a new + // module is pushed. However, the execution engine wants to take ownership + // of the module which does not map well to CodeGen's design. To work this + // around we created an empty module to make CodeGen happy. We should make + // sure it always stays empty. + assert((!CachedInCodeGenModule || (CachedInCodeGenModule->empty() && + CachedInCodeGenModule->global_empty() && + CachedInCodeGenModule->alias_empty() && + CachedInCodeGenModule->ifunc_empty())) && + "CodeGen wrote to a readonly module"); + std::unique_ptr M(CG->ReleaseModule()); + CG->StartModule("incr_module_" + std::to_string(ID++), M->getContext()); + return M; } - - return createInProcessRuntimeInterfaceBuilder(*this, Ctx, S); + return nullptr; } -namespace { - -class InterfaceKindVisitor - : public TypeVisitor { - friend class InProcessRuntimeInterfaceBuilder; - - ASTContext &Ctx; - Sema &S; - Expr *E; - llvm::SmallVector Args; - -public: - InterfaceKindVisitor(ASTContext &Ctx, Sema &S, Expr *E) - : Ctx(Ctx), S(S), E(E) {} - - Interpreter::InterfaceKind VisitRecordType(const RecordType *Ty) { - return Interpreter::InterfaceKind::WithAlloc; - } - - Interpreter::InterfaceKind - VisitMemberPointerType(const MemberPointerType *Ty) { - return Interpreter::InterfaceKind::WithAlloc; - } - - Interpreter::InterfaceKind - VisitConstantArrayType(const ConstantArrayType *Ty) { - return Interpreter::InterfaceKind::CopyArray; - } - - Interpreter::InterfaceKind - VisitFunctionProtoType(const FunctionProtoType *Ty) { - HandlePtrType(Ty); - return Interpreter::InterfaceKind::NoAlloc; - } - - Interpreter::InterfaceKind VisitPointerType(const PointerType *Ty) { - HandlePtrType(Ty); - return Interpreter::InterfaceKind::NoAlloc; - } - - Interpreter::InterfaceKind VisitReferenceType(const ReferenceType *Ty) { - ExprResult AddrOfE = S.CreateBuiltinUnaryOp(SourceLocation(), UO_AddrOf, E); - assert(!AddrOfE.isInvalid() && "Can not create unary expression"); - Args.push_back(AddrOfE.get()); - return Interpreter::InterfaceKind::NoAlloc; - } - - Interpreter::InterfaceKind VisitBuiltinType(const BuiltinType *Ty) { - if (Ty->isNullPtrType()) - Args.push_back(E); - else if (Ty->isFloatingType()) - Args.push_back(E); - else if (Ty->isIntegralOrEnumerationType()) - HandleIntegralOrEnumType(Ty); - else if (Ty->isVoidType()) { - // Do we need to still run `E`? - } - - return Interpreter::InterfaceKind::NoAlloc; - } - - Interpreter::InterfaceKind VisitEnumType(const EnumType *Ty) { - HandleIntegralOrEnumType(Ty); - return Interpreter::InterfaceKind::NoAlloc; - } - -private: - // Force cast these types to the uint that fits the register size. That way we - // reduce the number of overloads of `__clang_Interpreter_SetValueNoAlloc`. - void HandleIntegralOrEnumType(const Type *Ty) { - uint64_t PtrBits = Ctx.getTypeSize(Ctx.VoidPtrTy); - QualType UIntTy = Ctx.getBitIntType(/*Unsigned=*/true, PtrBits); - TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(UIntTy); - ExprResult CastedExpr = - S.BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), E); - assert(!CastedExpr.isInvalid() && "Cannot create cstyle cast expr"); - Args.push_back(CastedExpr.get()); - } - - void HandlePtrType(const Type *Ty) { - TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ctx.VoidPtrTy); - ExprResult CastedExpr = - S.BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), E); - assert(!CastedExpr.isInvalid() && "Can not create cstyle cast expression"); - Args.push_back(CastedExpr.get()); - } -}; - -class InProcessRuntimeInterfaceBuilder : public RuntimeInterfaceBuilder { - Interpreter &Interp; - ASTContext &Ctx; - Sema &S; - -public: - InProcessRuntimeInterfaceBuilder(Interpreter &Interp, ASTContext &C, Sema &S) - : Interp(Interp), Ctx(C), S(S) {} - - TransformExprFunction *getPrintValueTransformer() override { - return &transformForValuePrinting; - } - -private: - static ExprResult transformForValuePrinting(RuntimeInterfaceBuilder *Builder, - Expr *E, - ArrayRef FixedArgs) { - auto *B = static_cast(Builder); - - // Get rid of ExprWithCleanups. - if (auto *EWC = llvm::dyn_cast_if_present(E)) - E = EWC->getSubExpr(); - - InterfaceKindVisitor Visitor(B->Ctx, B->S, E); - - // The Interpreter* parameter and the out parameter `OutVal`. - for (Expr *E : FixedArgs) - Visitor.Args.push_back(E); - - QualType Ty = E->getType(); - QualType DesugaredTy = Ty.getDesugaredType(B->Ctx); - - // For lvalue struct, we treat it as a reference. - if (DesugaredTy->isRecordType() && E->isLValue()) { - DesugaredTy = B->Ctx.getLValueReferenceType(DesugaredTy); - Ty = B->Ctx.getLValueReferenceType(Ty); - } - - Expr *TypeArg = CStyleCastPtrExpr(B->S, B->Ctx.VoidPtrTy, - (uintptr_t)Ty.getAsOpaquePtr()); - // The QualType parameter `OpaqueType`, represented as `void*`. - Visitor.Args.push_back(TypeArg); - - // We push the last parameter based on the type of the Expr. Note we need - // special care for rvalue struct. - Interpreter::InterfaceKind Kind = Visitor.Visit(&*DesugaredTy); - switch (Kind) { - case Interpreter::InterfaceKind::WithAlloc: - case Interpreter::InterfaceKind::CopyArray: { - // __clang_Interpreter_SetValueWithAlloc. - ExprResult AllocCall = B->S.ActOnCallExpr( - /*Scope=*/nullptr, - B->Interp - .getValuePrintingInfo()[Interpreter::InterfaceKind::WithAlloc], - E->getBeginLoc(), Visitor.Args, E->getEndLoc()); - assert(!AllocCall.isInvalid() && "Can't create runtime interface call!"); - - TypeSourceInfo *TSI = - B->Ctx.getTrivialTypeSourceInfo(Ty, SourceLocation()); - - // Force CodeGen to emit destructor. - if (auto *RD = Ty->getAsCXXRecordDecl()) { - auto *Dtor = B->S.LookupDestructor(RD); - Dtor->addAttr(UsedAttr::CreateImplicit(B->Ctx)); - B->Interp.getCompilerInstance()->getASTConsumer().HandleTopLevelDecl( - DeclGroupRef(Dtor)); - } - - // __clang_Interpreter_SetValueCopyArr. - if (Kind == Interpreter::InterfaceKind::CopyArray) { - const auto *ConstantArrTy = - cast(DesugaredTy.getTypePtr()); - size_t ArrSize = B->Ctx.getConstantArrayElementCount(ConstantArrTy); - Expr *ArrSizeExpr = IntegerLiteralExpr(B->Ctx, ArrSize); - Expr *Args[] = {E, AllocCall.get(), ArrSizeExpr}; - return B->S.ActOnCallExpr( - /*Scope *=*/nullptr, - B->Interp - .getValuePrintingInfo()[Interpreter::InterfaceKind::CopyArray], - SourceLocation(), Args, SourceLocation()); - } - Expr *Args[] = { - AllocCall.get(), - B->Interp.getValuePrintingInfo()[Interpreter::InterfaceKind::NewTag]}; - ExprResult CXXNewCall = B->S.BuildCXXNew( - E->getSourceRange(), - /*UseGlobal=*/true, /*PlacementLParen=*/SourceLocation(), Args, - /*PlacementRParen=*/SourceLocation(), - /*TypeIdParens=*/SourceRange(), TSI->getType(), TSI, std::nullopt, - E->getSourceRange(), E); - - assert(!CXXNewCall.isInvalid() && - "Can't create runtime placement new call!"); - - return B->S.ActOnFinishFullExpr(CXXNewCall.get(), - /*DiscardedValue=*/false); - } - // __clang_Interpreter_SetValueNoAlloc. - case Interpreter::InterfaceKind::NoAlloc: { - return B->S.ActOnCallExpr( - /*Scope=*/nullptr, - B->Interp.getValuePrintingInfo()[Interpreter::InterfaceKind::NoAlloc], - E->getBeginLoc(), Visitor.Args, E->getEndLoc()); - } - default: - llvm_unreachable("Unhandled Interpreter::InterfaceKind"); - } - } -}; -} // namespace - -static std::unique_ptr -createInProcessRuntimeInterfaceBuilder(Interpreter &Interp, ASTContext &Ctx, - Sema &S) { - return std::make_unique(Interp, Ctx, S); -} - -// This synthesizes a call expression to a speciall -// function that is responsible for generating the Value. -// In general, we transform: -// clang-repl> x -// To: -// // 1. If x is a built-in type like int, float. -// __clang_Interpreter_SetValueNoAlloc(ThisInterp, OpaqueValue, xQualType, x); -// // 2. If x is a struct, and a lvalue. -// __clang_Interpreter_SetValueNoAlloc(ThisInterp, OpaqueValue, xQualType, -// &x); -// // 3. If x is a struct, but a rvalue. -// new (__clang_Interpreter_SetValueWithAlloc(ThisInterp, OpaqueValue, -// xQualType)) (x); - -Expr *Interpreter::SynthesizeExpr(Expr *E) { - Sema &S = getCompilerInstance()->getSema(); - ASTContext &Ctx = S.getASTContext(); - - if (!RuntimeIB) { - RuntimeIB = FindRuntimeInterface(); - AddPrintValueCall = RuntimeIB->getPrintValueTransformer(); - } - - assert(AddPrintValueCall && - "We don't have a runtime interface for pretty print!"); - - // Create parameter `ThisInterp`. - auto *ThisInterp = CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)this); - - // Create parameter `OutVal`. - auto *OutValue = CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)&LastValue); - - // Build `__clang_Interpreter_SetValue*` call. - ExprResult Result = - AddPrintValueCall(RuntimeIB.get(), E, {ThisInterp, OutValue}); - - // It could fail, like printing an array type in C. (not supported) - if (Result.isInvalid()) - return E; - return Result.get(); -} - -// Temporary rvalue struct that need special care. -REPL_EXTERNAL_VISIBILITY void * -__clang_Interpreter_SetValueWithAlloc(void *This, void *OutVal, - void *OpaqueType) { - Value &VRef = *(Value *)OutVal; - VRef = Value(static_cast(This), OpaqueType); - return VRef.getPtr(); -} - -extern "C" void REPL_EXTERNAL_VISIBILITY __clang_Interpreter_SetValueNoAlloc( - void *This, void *OutVal, void *OpaqueType, ...) { - Value &VRef = *(Value *)OutVal; - Interpreter *I = static_cast(This); - VRef = Value(I, OpaqueType); - if (VRef.isVoid()) - return; - - va_list args; - va_start(args, /*last named param*/ OpaqueType); - - QualType QT = VRef.getType(); - if (VRef.getKind() == Value::K_PtrOrObj) { - VRef.setPtr(va_arg(args, void *)); - } else { - if (const auto *ET = QT->getAs()) - QT = ET->getDecl()->getIntegerType(); - switch (QT->castAs()->getKind()) { - default: - llvm_unreachable("unknown type kind!"); - break; - // Types shorter than int are resolved as int, else va_arg has UB. - case BuiltinType::Bool: - VRef.setBool(va_arg(args, int)); - break; - case BuiltinType::Char_S: - VRef.setChar_S(va_arg(args, int)); - break; - case BuiltinType::SChar: - VRef.setSChar(va_arg(args, int)); - break; - case BuiltinType::Char_U: - VRef.setChar_U(va_arg(args, unsigned)); - break; - case BuiltinType::UChar: - VRef.setUChar(va_arg(args, unsigned)); - break; - case BuiltinType::Short: - VRef.setShort(va_arg(args, int)); - break; - case BuiltinType::UShort: - VRef.setUShort(va_arg(args, unsigned)); - break; - case BuiltinType::Int: - VRef.setInt(va_arg(args, int)); - break; - case BuiltinType::UInt: - VRef.setUInt(va_arg(args, unsigned)); - break; - case BuiltinType::Long: - VRef.setLong(va_arg(args, long)); - break; - case BuiltinType::ULong: - VRef.setULong(va_arg(args, unsigned long)); - break; - case BuiltinType::LongLong: - VRef.setLongLong(va_arg(args, long long)); - break; - case BuiltinType::ULongLong: - VRef.setULongLong(va_arg(args, unsigned long long)); - break; - // Types shorter than double are resolved as double, else va_arg has UB. - case BuiltinType::Float: - VRef.setFloat(va_arg(args, double)); - break; - case BuiltinType::Double: - VRef.setDouble(va_arg(args, double)); - break; - case BuiltinType::LongDouble: - VRef.setLongDouble(va_arg(args, long double)); - break; - // See REPL_BUILTIN_TYPES. - } - } - va_end(args); +CodeGenerator *Interpreter::getCodeGen() const { + FrontendAction *WrappedAct = Act->getWrapped(); + if (!WrappedAct->hasIRSupport()) + return nullptr; + return static_cast(WrappedAct)->getCodeGenerator(); } -// A trampoline to work around the fact that operator placement new cannot -// really be forward declared due to libc++ and libstdc++ declaration mismatch. -// FIXME: __clang_Interpreter_NewTag is ODR violation because we get the same -// definition in the interpreter runtime. We should move it in a runtime header -// which gets included by the interpreter and here. -struct __clang_Interpreter_NewTag {}; -REPL_EXTERNAL_VISIBILITY void * -operator new(size_t __sz, void *__p, __clang_Interpreter_NewTag) noexcept { - // Just forward to the standard operator placement new. - return operator new(__sz, __p); -} +} // end namespace clang diff --git a/clang/lib/Interpreter/InterpreterUtils.cpp b/clang/lib/Interpreter/InterpreterUtils.cpp index 45f6322b8461e..2cfbb2fcb4506 100644 --- a/clang/lib/Interpreter/InterpreterUtils.cpp +++ b/clang/lib/Interpreter/InterpreterUtils.cpp @@ -81,7 +81,7 @@ NamedDecl *LookupNamed(Sema &S, llvm::StringRef Name, else { const DeclContext *PrimaryWithin = nullptr; if (const auto *TD = dyn_cast(Within)) - PrimaryWithin = llvm::dyn_cast_or_null(TD->getDefinition()); + PrimaryWithin = dyn_cast_if_present(TD->getDefinition()); else PrimaryWithin = Within->getPrimaryContext(); @@ -97,15 +97,404 @@ NamedDecl *LookupNamed(Sema &S, llvm::StringRef Name, R.resolveKind(); if (R.isSingleResult()) - return llvm::dyn_cast(R.getFoundDecl()); + return dyn_cast(R.getFoundDecl()); return nullptr; } +static NestedNameSpecifier *CreateOuterNNS(const ASTContext &Ctx, const Decl *D, + bool FullyQualify) { + const DeclContext *DC = D->getDeclContext(); + if (const auto *NS = dyn_cast(DC)) { + while (NS && NS->isInline()) { + // Ignore inline namespace; + NS = dyn_cast_if_present(NS->getDeclContext()); + } + if (NS && NS->getDeclName()) + return CreateNestedNameSpecifier(Ctx, NS); + return nullptr; // no starting '::', no anonymous + } + if (const auto *TD = dyn_cast(DC)) + return CreateNestedNameSpecifier(Ctx, TD, FullyQualify); + if (const auto *TDD = dyn_cast(DC)) + return CreateNestedNameSpecifier(Ctx, TDD, FullyQualify); + return nullptr; // no starting '::' +} + +static NestedNameSpecifier * +CreateNestedNameSpecifierForScopeOf(const ASTContext &Ctx, const Decl *D, + bool FullyQualified) { + // Create a nested name specifier for the declaring context of the type. + + assert(D); + + const auto *Outer = dyn_cast_if_present(D->getDeclContext()); + const auto *OuterNs = dyn_cast_if_present(D->getDeclContext()); + if (Outer && !(OuterNs && OuterNs->isAnonymousNamespace())) { + + if (const auto *CXXD = dyn_cast(D->getDeclContext())) { + + if (ClassTemplateDecl *CTD = CXXD->getDescribedClassTemplate()) { + // We are in the case of a type(def) that was declared in a + // class template but is *not* type dependent. In clang, it gets + // attached to the class template declaration rather than any + // specific class template instantiation. This result in 'odd' + // fully qualified typename: + // vector<_Tp,_Alloc>::size_type + // Make the situation is 'useable' but looking a bit odd by + // picking a random instance as the declaring context. + // FIXME: We should not use the iterators here to check if we are in + // a template specialization. clTempl != cxxdecl already tell us that + // is the case. It seems that we rely on a side-effect from triggering + // deserializations to support 'some' use-case. See ROOT-9709. + if (CTD->spec_begin() != CTD->spec_end()) { + D = *(CTD->spec_begin()); + Outer = dyn_cast(D); + OuterNs = dyn_cast(D); + } + } + } + + if (OuterNs) + return CreateNestedNameSpecifier(Ctx, OuterNs); + + if (const auto *TD = dyn_cast(Outer)) + return CreateNestedNameSpecifier(Ctx, TD, FullyQualified); + } + return nullptr; +} + +static NestedNameSpecifier * +CreateNestedNameSpecifierForScopeOf(const ASTContext &Ctx, const Type *TypePtr, + bool FullyQualified) { + // Create a nested name specifier for the declaring context of the type. + + if (!TypePtr) + return nullptr; + + Decl *D = nullptr; + if (const auto *TDT = dyn_cast(TypePtr)) { + D = TDT->getDecl(); + } else { + // There are probably other cases ... + if (const auto *TT = dyn_cast_if_present(TypePtr)) + D = TT->getDecl(); + else + D = TypePtr->getAsCXXRecordDecl(); + } + + if (!D) + return nullptr; + + return CreateNestedNameSpecifierForScopeOf(Ctx, D, FullyQualified); +} + +static NestedNameSpecifier * +GetFullyQualifiedNameSpecifier(const ASTContext &Ctx, + NestedNameSpecifier *Scope) { + // Return a fully qualified version of this name specifier + if (Scope->getKind() == NestedNameSpecifier::Global) { + // Already fully qualified. + return Scope; + } + + if (const Type *Type = Scope->getAsType()) { + // Find decl context. + const TagDecl *TD = nullptr; + if (const auto *TT = dyn_cast(Type)) + TD = TT->getDecl(); + else + TD = Type->getAsCXXRecordDecl(); + + if (TD) + return CreateNestedNameSpecifier(Ctx, TD, true /*FullyQualified*/); + + if (const auto *TDD = dyn_cast(Type)) + return CreateNestedNameSpecifier(Ctx, TDD->getDecl(), + true /*FullyQualified*/); + } else if (const NamespaceDecl *NS = Scope->getAsNamespace()) + return CreateNestedNameSpecifier(Ctx, NS); + else if (const auto *Alias = Scope->getAsNamespaceAlias()) + return CreateNestedNameSpecifier(Ctx, + Alias->getNamespace()->getCanonicalDecl()); + + return Scope; +} + +static bool GetFullyQualifiedTemplateName(const ASTContext &Ctx, + TemplateName &Name) { + + bool Changed = false; + NestedNameSpecifier *NNS = nullptr; + + TemplateDecl *TD = Name.getAsTemplateDecl(); + QualifiedTemplateName *QTN = Name.getAsQualifiedTemplateName(); + + if (QTN && !QTN->hasTemplateKeyword()) { + NNS = QTN->getQualifier(); + NestedNameSpecifier *QNNS = GetFullyQualifiedNameSpecifier(Ctx, NNS); + if (QNNS != NNS) { + Changed = true; + NNS = QNNS; + } else { + NNS = nullptr; + } + } else { + NNS = CreateNestedNameSpecifierForScopeOf(Ctx, TD, true); + } + if (NNS) { + Name = Ctx.getQualifiedTemplateName(NNS, + /*TemplateKeyword=*/false, + TemplateName(TD)); + Changed = true; + } + return Changed; +} + +static bool GetFullyQualifiedTemplateArgument(const ASTContext &Ctx, + TemplateArgument &Arg) { + bool Changed = false; + + // Note: we do not handle TemplateArgument::Expression, to replace it + // we need the information for the template instance decl. + // See GetPartiallyDesugaredTypeImpl + + if (Arg.getKind() == TemplateArgument::Template) { + TemplateName Name = Arg.getAsTemplate(); + Changed = GetFullyQualifiedTemplateName(Ctx, Name); + if (Changed) { + Arg = TemplateArgument(Name); + } + } else if (Arg.getKind() == TemplateArgument::Type) { + QualType SubTy = Arg.getAsType(); + // Check if the type needs more desugaring and recurse. + QualType QTFQ = GetFullyQualifiedType(SubTy, Ctx); + if (QTFQ != SubTy) { + Arg = TemplateArgument(QTFQ); + Changed = true; + } + } else if (Arg.getKind() == TemplateArgument::Pack) { + SmallVector desArgs; + for (auto I = Arg.pack_begin(), E = Arg.pack_end(); I != E; ++I) { + TemplateArgument PackArg(*I); + Changed = GetFullyQualifiedTemplateArgument(Ctx, PackArg); + desArgs.push_back(PackArg); + } + if (Changed) { + // The allocator in ASTContext is mutable ... + // Keep the argument const to be inline will all the other interfaces + // like: NestedNameSpecifier::Create + ASTContext &MutableCtx(const_cast(Ctx)); + Arg = TemplateArgument::CreatePackCopy(MutableCtx, desArgs); + } + } + return Changed; +} + +static const Type *GetFullyQualifiedLocalType(const ASTContext &Ctx, + const Type *Typeptr) { + // We really just want to handle the template parameter if any .... + // In case of template specializations iterate over the arguments and + // fully qualify them as well. + if (const auto *TST = dyn_cast(Typeptr)) { + + bool MightHaveChanged = false; + llvm::SmallVector DesArgs; + for (auto &I : TST->template_arguments()) { + + // cheap to copy and potentially modified by + // GetFullyQualifedTemplateArgument + TemplateArgument Arg(I); + MightHaveChanged |= GetFullyQualifiedTemplateArgument(Ctx, Arg); + DesArgs.push_back(Arg); + } + + // If desugaring happened allocate new type in the AST. + if (MightHaveChanged) { + QualType QT = Ctx.getTemplateSpecializationType( + TST->getTemplateName(), DesArgs, TST->getCanonicalTypeInternal()); + return QT.getTypePtr(); + } + } else if (const auto *TSTRecord = dyn_cast(Typeptr)) { + // We are asked to fully qualify and we have a Record Type, + // which can point to a template instantiation with no sugar in any of + // its template argument, however we still need to fully qualify them. + + if (const auto *TSTdecl = + dyn_cast(TSTRecord->getDecl())) { + const TemplateArgumentList &TemplateArgs = TSTdecl->getTemplateArgs(); + + bool MightHaveChanged = false; + llvm::SmallVector DesArgs; + for (unsigned int I = 0, E = TemplateArgs.size(); I != E; ++I) { + + // cheap to copy and potentially modified by + // GetFullyQualifedTemplateArgument + TemplateArgument Arg(TemplateArgs[I]); + MightHaveChanged |= GetFullyQualifiedTemplateArgument(Ctx, Arg); + DesArgs.push_back(Arg); + } + + // If desugaring happened allocate new type in the AST. + if (MightHaveChanged) { + TemplateName TN(TSTdecl->getSpecializedTemplate()); + QualType QT = Ctx.getTemplateSpecializationType( + TN, DesArgs, TSTRecord->getCanonicalTypeInternal()); + return QT.getTypePtr(); + } + } + } + return Typeptr; +} + +NestedNameSpecifier *CreateNestedNameSpecifier(const ASTContext &Ctx, + const NamespaceDecl *NSD) { + while (NSD && NSD->isInline()) { + // Ignore inline namespace; + NSD = dyn_cast_if_present(NSD->getDeclContext()); + } + if (!NSD) + return nullptr; + + bool FullyQualified = true; // doesn't matter, DeclContexts are namespaces + return NestedNameSpecifier::Create( + Ctx, CreateOuterNNS(Ctx, NSD, FullyQualified), NSD); +} + +NestedNameSpecifier *CreateNestedNameSpecifier(const ASTContext &Ctx, + const TypedefNameDecl *TD, + bool FullyQualify) { + return NestedNameSpecifier::Create(Ctx, CreateOuterNNS(Ctx, TD, FullyQualify), + /*Template=*/true, TD->getTypeForDecl()); +} + +NestedNameSpecifier *CreateNestedNameSpecifier(const ASTContext &Ctx, + const TagDecl *TD, + bool FullyQualify) { + const Type *Ty = Ctx.getTypeDeclType(TD).getTypePtr(); + if (FullyQualify) + Ty = GetFullyQualifiedLocalType(Ctx, Ty); + return NestedNameSpecifier::Create(Ctx, CreateOuterNNS(Ctx, TD, FullyQualify), + /*Template=*/false, Ty); +} + +QualType GetFullyQualifiedType(QualType QT, const ASTContext &Ctx) { + // Return the fully qualified type, if we need to recurse through any + // template parameter, this needs to be merged somehow with + // GetPartialDesugaredType. + + // In case of myType* we need to strip the pointer first, fully qualifiy + // and attach the pointer once again. + if (isa(QT.getTypePtr())) { + // Get the qualifiers. + Qualifiers Quals = QT.getQualifiers(); + QT = GetFullyQualifiedType(QT->getPointeeType(), Ctx); + QT = Ctx.getPointerType(QT); + // Add back the qualifiers. + QT = Ctx.getQualifiedType(QT, Quals); + return QT; + } + + // In case of myType& we need to strip the pointer first, fully qualifiy + // and attach the pointer once again. + if (isa(QT.getTypePtr())) { + // Get the qualifiers. + bool IsLValueRefTy = isa(QT.getTypePtr()); + Qualifiers Quals = QT.getQualifiers(); + QT = GetFullyQualifiedType(QT->getPointeeType(), Ctx); + // Add the r- or l-value reference type back to the desugared one. + if (IsLValueRefTy) + QT = Ctx.getLValueReferenceType(QT); + else + QT = Ctx.getRValueReferenceType(QT); + // Add back the qualifiers. + QT = Ctx.getQualifiedType(QT, Quals); + return QT; + } + + // Strip deduced types. + if (const auto *AutoTy = dyn_cast(QT.getTypePtr())) { + if (!AutoTy->getDeducedType().isNull()) + return GetFullyQualifiedType( + AutoTy->getDeducedType().getDesugaredType(Ctx), Ctx); + } + + // Remove the part of the type related to the type being a template + // parameter (we won't report it as part of the 'type name' and it is + // actually make the code below to be more complex (to handle those) + while (isa(QT.getTypePtr())) { + // Get the qualifiers. + Qualifiers Quals = QT.getQualifiers(); + + QT = cast(QT.getTypePtr())->desugar(); + + // Add back the qualifiers. + QT = Ctx.getQualifiedType(QT, Quals); + } + + NestedNameSpecifier *Prefix = nullptr; + Qualifiers PrefixQualifiers; + if (const auto *EType = dyn_cast(QT.getTypePtr())) { + // Intentionally, we do not care about the other compononent of + // the elaborated type (the keyword) as part of the partial + // desugaring (and/or name normalization) is to remove it. + Prefix = EType->getQualifier(); + if (Prefix) { + const NamespaceDecl *NS = Prefix->getAsNamespace(); + if (Prefix != NestedNameSpecifier::GlobalSpecifier(Ctx) && + !(NS && NS->isAnonymousNamespace())) { + PrefixQualifiers = QT.getLocalQualifiers(); + Prefix = GetFullyQualifiedNameSpecifier(Ctx, Prefix); + QT = QualType(EType->getNamedType().getTypePtr(), 0); + } else { + Prefix = nullptr; + } + } + } else { + + // Create a nested name specifier if needed (i.e. if the decl context + // is not the global scope. + Prefix = CreateNestedNameSpecifierForScopeOf(Ctx, QT.getTypePtr(), + true /*FullyQualified*/); + + // move the qualifiers on the outer type (avoid 'std::const string'!) + if (Prefix) { + PrefixQualifiers = QT.getLocalQualifiers(); + QT = QualType(QT.getTypePtr(), 0); + } + } + + // In case of template specializations iterate over the arguments and + // fully qualify them as well. + if (isa(QT.getTypePtr())) { + + Qualifiers Qualifiers = QT.getLocalQualifiers(); + const Type *TypePtr = GetFullyQualifiedLocalType(Ctx, QT.getTypePtr()); + QT = Ctx.getQualifiedType(TypePtr, Qualifiers); + + } else if (isa(QT.getTypePtr())) { + // We are asked to fully qualify and we have a Record Type, + // which can point to a template instantiation with no sugar in any of + // its template argument, however we still need to fully qualify them. + + Qualifiers Qualifiers = QT.getLocalQualifiers(); + const Type *TypePtr = GetFullyQualifiedLocalType(Ctx, QT.getTypePtr()); + QT = Ctx.getQualifiedType(TypePtr, Qualifiers); + } + if (Prefix) { + // We intentionally always use ElaboratedTypeKeyword::None, we never want + // the keyword (humm ... what about anonymous types?) + QT = Ctx.getElaboratedType(ElaboratedTypeKeyword::None, Prefix, QT); + QT = Ctx.getQualifiedType(QT, PrefixQualifiers); + } + return QT; +} + std::string GetFullTypeName(ASTContext &Ctx, QualType QT) { + QualType FQT = GetFullyQualifiedType(QT, Ctx); PrintingPolicy Policy(Ctx.getPrintingPolicy()); Policy.SuppressScope = false; Policy.AnonymousTagLocations = false; - return QT.getAsString(Policy); + return FQT.getAsString(Policy); } } // namespace clang diff --git a/clang/lib/Interpreter/InterpreterUtils.h b/clang/lib/Interpreter/InterpreterUtils.h index c7b405b486d93..bd23f249551b5 100644 --- a/clang/lib/Interpreter/InterpreterUtils.h +++ b/clang/lib/Interpreter/InterpreterUtils.h @@ -45,9 +45,27 @@ NamespaceDecl *LookupNamespace(Sema &S, llvm::StringRef Name, const DeclContext *Within = nullptr); NamedDecl *LookupNamed(Sema &S, llvm::StringRef Name, - const DeclContext *Within); + const DeclContext *Within = nullptr); + +NestedNameSpecifier *CreateNestedNameSpecifier(const ASTContext &Ctx, + const NamespaceDecl *Namesp); + +NestedNameSpecifier *CreateNestedNameSpecifier(const ASTContext &Ctx, + const TypedefNameDecl *TD, + bool FullyQualify); + +NestedNameSpecifier *CreateNestedNameSpecifier(const ASTContext &Ctx, + const TagDecl *TD, + bool FullyQualify); + +QualType GetFullyQualifiedType(QualType QT, const ASTContext &Ctx); std::string GetFullTypeName(ASTContext &Ctx, QualType QT); + +class Value; + +std::string ReplPrintTypeImpl(const Value &); +std::string ReplPrintDataImpl(const Value &); } // namespace clang #endif diff --git a/clang/lib/Interpreter/InterpreterValuePrinter.cpp b/clang/lib/Interpreter/InterpreterValuePrinter.cpp new file mode 100644 index 0000000000000..df2ee3edcc156 --- /dev/null +++ b/clang/lib/Interpreter/InterpreterValuePrinter.cpp @@ -0,0 +1,908 @@ +//===--- InterpreterValuePrinter.cpp - Value printing utils -----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements routines for in-process value printing in clang-repl. +// +//===----------------------------------------------------------------------===// + +#include "IncrementalParser.h" +#include "InterpreterUtils.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/PrettyPrinter.h" +#include "clang/AST/QualTypeNames.h" +#include "clang/AST/Type.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Interpreter/Interpreter.h" +#include "clang/Interpreter/Value.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Sema/Lookup.h" +#include "clang/Sema/Sema.h" + +#include "llvm/Support/Error.h" +#include "llvm/Support/raw_ostream.h" + +#include +#include +#include +#include + +#define DEBUG_TYPE "interp-value" + +using namespace clang; + +static std::string DeclTypeToString(const QualType &QT, NamedDecl *D) { + std::string Str; + llvm::raw_string_ostream SS(Str); + if (QT.hasQualifiers()) + SS << QT.getQualifiers().getAsString() << " "; + SS << D->getQualifiedNameAsString(); + return Str; +} + +static std::string QualTypeToString(ASTContext &Ctx, QualType QT) { + PrintingPolicy Policy(Ctx.getPrintingPolicy()); + // Print the Allocator in STL containers, for instance. + Policy.SuppressDefaultTemplateArgs = false; + Policy.SuppressUnwrittenScope = true; + // Print 'a >' rather than 'a>'. + Policy.SplitTemplateClosers = true; + + struct LocalPrintingPolicyRAII { + ASTContext &Context; + PrintingPolicy Policy; + + LocalPrintingPolicyRAII(ASTContext &Ctx, PrintingPolicy &PP) + : Context(Ctx), Policy(Ctx.getPrintingPolicy()) { + Context.setPrintingPolicy(PP); + } + ~LocalPrintingPolicyRAII() { Context.setPrintingPolicy(Policy); } + } X(Ctx, Policy); + + const QualType NonRefTy = QT.getNonReferenceType(); + + if (const auto *TTy = llvm::dyn_cast(NonRefTy)) + return DeclTypeToString(NonRefTy, TTy->getDecl()); + + if (const auto *TRy = dyn_cast(NonRefTy)) + return DeclTypeToString(NonRefTy, TRy->getDecl()); + + const QualType Canon = NonRefTy.getCanonicalType(); + + // FIXME: How a builtin type can be a function pointer type? + if (Canon->isBuiltinType() && !NonRefTy->isFunctionPointerType() && + !NonRefTy->isMemberPointerType()) + return Canon.getAsString(Ctx.getPrintingPolicy()); + + if (const auto *TDTy = dyn_cast(NonRefTy)) { + // FIXME: TemplateSpecializationType & SubstTemplateTypeParmType checks + // are predominately to get STL containers to print nicer and might be + // better handled in GetFullyQualifiedName. + // + // std::vector::iterator is a TemplateSpecializationType + // std::vector::value_type is a SubstTemplateTypeParmType + // + QualType SSDesugar = TDTy->getLocallyUnqualifiedSingleStepDesugaredType(); + if (llvm::isa(SSDesugar)) + return GetFullTypeName(Ctx, Canon); + else if (llvm::isa(SSDesugar)) + return GetFullTypeName(Ctx, NonRefTy); + return DeclTypeToString(NonRefTy, TDTy->getDecl()); + } + return GetFullTypeName(Ctx, NonRefTy); +} + +static std::string EnumToString(const Value &V) { + std::string Str; + llvm::raw_string_ostream SS(Str); + ASTContext &Ctx = const_cast(V.getASTContext()); + + QualType DesugaredTy = V.getType().getDesugaredType(Ctx); + const EnumType *EnumTy = DesugaredTy.getNonReferenceType()->getAs(); + assert(EnumTy && "Fail to cast to enum type"); + + EnumDecl *ED = EnumTy->getDecl(); + uint64_t Data = V.getULongLong(); + bool IsFirst = true; + llvm::APSInt AP = Ctx.MakeIntValue(Data, DesugaredTy); + + for (auto I = ED->enumerator_begin(), E = ED->enumerator_end(); I != E; ++I) { + if (I->getInitVal() == AP) { + if (!IsFirst) + SS << " ? "; + SS << "(" + I->getQualifiedNameAsString() << ")"; + IsFirst = false; + } + } + llvm::SmallString<64> APStr; + AP.toString(APStr, /*Radix=*/10); + SS << " : " << QualTypeToString(Ctx, ED->getIntegerType()) << " " << APStr; + return Str; +} + +static std::string FunctionToString(const Value &V, const void *Ptr) { + std::string Str; + llvm::raw_string_ostream SS(Str); + SS << "Function @" << Ptr; + + const FunctionDecl *FD = nullptr; + + auto Decls = V.getASTContext().getTranslationUnitDecl()->decls(); + assert(std::distance(Decls.begin(), Decls.end()) == 1 && + "TU should only contain one Decl"); + auto *TLSD = llvm::cast(*Decls.begin()); + + // Get __clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void + // *OpaqueType, void *Val); + if (auto *InterfaceCall = llvm::dyn_cast(TLSD->getStmt())) { + const auto *Arg = InterfaceCall->getArg(/*Val*/ 3); + // Get rid of cast nodes. + while (const CastExpr *CastE = llvm::dyn_cast(Arg)) + Arg = CastE->getSubExpr(); + if (const DeclRefExpr *DeclRefExp = llvm::dyn_cast(Arg)) + FD = llvm::dyn_cast(DeclRefExp->getDecl()); + + if (FD) { + SS << '\n'; + const clang::FunctionDecl *FDef; + if (FD->hasBody(FDef)) + FDef->print(SS); + } + } + return Str; +} + +static std::string AddressToString(const void *Ptr, char Prefix) { + std::string Str; + llvm::raw_string_ostream SS(Str); + if (!Ptr) + return Str; + SS << Prefix << Ptr; + return Str; +} + +// Build the CallExpr to `PrintValueRuntime`. +static llvm::Error BuildWrapperBody(LookupResult &R, Sema &S, ASTContext &Ctx, + FunctionDecl *WrapperFD, QualType QT, + const void *ValPtr) { + Sema::SynthesizedFunctionScope SemaFScope(S, WrapperFD); + clang::DeclarationName RuntimeCallName; + + Expr *OverldExpr = UnresolvedLookupExpr::Create( + Ctx, /*NamingClass=*/nullptr, NestedNameSpecifierLoc(), + clang::DeclarationNameInfo(RuntimeCallName, SourceLocation()), + /*RequiresADL=*/false, R.begin(), R.end(), /*KnownDependent=*/false, + /*KnownInstantiationDependent=*/false); + + if (const auto *AT = llvm::dyn_cast(QT.getTypePtr())) { + if (AT->isDeduced()) + QT = AT->getDeducedType().getDesugaredType(Ctx); + } + + if (const auto *PT = llvm::dyn_cast(QT.getTypePtr())) { + // Normalize `X*` to `const void*`, invoke `printValue(const void**)`, + // unless it's a character string. + QualType QTPointeeUnqual = PT->getPointeeType().getUnqualifiedType(); + if (!Ctx.hasSameType(QTPointeeUnqual, Ctx.CharTy) && + !Ctx.hasSameType(QTPointeeUnqual, Ctx.WCharTy) && + !Ctx.hasSameType(QTPointeeUnqual, Ctx.Char16Ty) && + !Ctx.hasSameType(QTPointeeUnqual, Ctx.Char32Ty)) { + QT = Ctx.getPointerType(Ctx.VoidTy.withConst()); + } + } else if (const auto *RTy = llvm::dyn_cast(QT.getTypePtr())) { + // X& will be printed as X* (the pointer will be added below). + QT = RTy->getPointeeType(); + // Val will be a X**, but we cast this to X*, so dereference here: + ValPtr = *(const void *const *)ValPtr; + } + + // `PrintValueRuntime()` takes the *address* of the value to be printed: + QualType QTPtr = Ctx.getPointerType(QT); + Expr *TypeArg = CStyleCastPtrExpr(S, QTPtr, (uintptr_t)ValPtr); + llvm::SmallVector CallArgs = {TypeArg}; + + // Create the CallExpr. + ExprResult RuntimeCall = + S.ActOnCallExpr(S.getCurScope(), OverldExpr, SourceLocation(), CallArgs, + SourceLocation()); + if (RuntimeCall.isInvalid()) { + std::string Name = R.getLookupName().getAsString(); + return llvm::make_error("Cannot create call to " + Name, + llvm::inconvertibleErrorCode()); + } + + // Create the ReturnStmt. + StmtResult RetStmt = + S.ActOnReturnStmt(SourceLocation(), RuntimeCall.get(), S.getCurScope()); + + if (RetStmt.isInvalid()) + return llvm::make_error("Cannot create a return stmt", + llvm::inconvertibleErrorCode()); + + // Create the CompoundStmt. + StmtResult Body = + CompoundStmt::Create(Ctx, {RetStmt.get()}, FPOptionsOverride(), + SourceLocation(), SourceLocation()); + if (Body.isInvalid()) + return llvm::make_error("Cannot create function body", + llvm::inconvertibleErrorCode()); + + WrapperFD->setBody(Body.get()); + // Add attribute `__attribute__((used))`. + WrapperFD->addAttr(UsedAttr::CreateImplicit(Ctx)); + + return llvm::Error::success(); +} + +static constexpr const char *const WrapperName = "__InterpreterCallPrint"; + +static llvm::Expected CompileDecl(Interpreter &Interp, + Decl *D) { + assert(D && "The Decl being compiled can't be null"); + + ASTConsumer &Consumer = Interp.getCompilerInstance()->getASTConsumer(); + Consumer.HandleTopLevelDecl(DeclGroupRef(D)); + Interp.getCompilerInstance()->getSema().PerformPendingInstantiations(); + ASTContext &C = Interp.getASTContext(); + TranslationUnitDecl *TUPart = C.getTranslationUnitDecl(); + assert(!TUPart->containsDecl(D) && "Decl already added!"); + TUPart->addDecl(D); + Consumer.HandleTranslationUnit(C); + + if (std::unique_ptr M = Interp.GenModule()) { + PartialTranslationUnit PTU = {TUPart, std::move(M)}; + if (llvm::Error Err = Interp.Execute(PTU)) + return Err; + ASTNameGenerator ASTNameGen(Interp.getASTContext()); + llvm::Expected AddrOrErr = + Interp.getSymbolAddressFromLinkerName(ASTNameGen.getName(D)); + + return AddrOrErr; + } + return llvm::orc::ExecutorAddr{}; +} + +static std::string CreateUniqName(std::string Base) { + static size_t I = 0; + Base += std::to_string(I); + I += 1; + return Base; +} + +// Check if the user has implemented a function that avoids fallback to +// defaults. +static clang::LookupResult LookupUserDefined(Sema &S, QualType QT) { + DeclarationName Name; + ASTContext &Ctx = S.getASTContext(); + if (S.getLangOpts().CPlusPlus) { + if (auto *NSD = dyn_cast_or_null(LookupNamed(S, "caas"))) + if (NamedDecl* RtD = LookupNamed(S, "runtime", NSD)) { + Name = S.PP.getIdentifierInfo("to_string"); + LookupResult R(S, Name, SourceLocation(), Sema::LookupOrdinaryName); + S.LookupQualifiedName(R, cast(RtD)->getPrimaryContext()); + LookupResult::Filter F = R.makeFilter(); + while (F.hasNext()) { + NamedDecl* D = F.next(); + FunctionDecl *FD = dyn_cast(D); + if (!FD && isa(D)) + FD = cast(D)->getTemplatedDecl(); + if (FD) + if (FD->getNumParams() == 1) { + QualType ParamTy = FD->getParamDecl(0)->getType(); + if (ParamTy->isPointerOrReferenceType()) { + // It is a uninstantiated template, we can't really match types. + if (isa(D)) + continue; + ParamTy = ParamTy->getPointeeType(); + if (Ctx.hasSameUnqualifiedType(ParamTy, QT)) + continue; + } + } + + F.erase(); + } + F.done(); + return R; + } + } else { + PrintingPolicy Policy = S.getASTContext().getPrintingPolicy(); + Policy.SuppressElaboration = 1; + Policy.SuppressTagKeyword = 1; + Policy.FullyQualifiedName = 1; + std::string TypeStr = QT.getAsString(Policy); + Name = S.PP.getIdentifierInfo("caas_runtime_to_string_" + TypeStr); + + LLVM_DEBUG(llvm::dbgs() << "Looking for user-defined '" << Name << "'"); + } + LookupResult R(S, Name, SourceLocation(), Sema::LookupOrdinaryName); + S.LookupName(R, S.getCurScope()); + return R; +} + +namespace clang { + +std::string Interpreter::CallUserSpecifiedPrinter(LookupResult &R, const Value &V) { + assert(!R.empty()); + // if (!R.isSingleResult()) + // return "{error: multiple results for '" + R.getLookupName().getAsString() + + // "'}"; + + Sema &S = getCompilerInstance()->getSema(); + ASTContext &Ctx = S.getASTContext(); + + // Check the found candidates. + QualType RetTy = Ctx.getPointerType(Ctx.CharTy.withConst()); + if (Ctx.getLangOpts().CPlusPlus) { + if (!S.StdNamespace) + return "{error: user-defined pretty printers require std::string; consider #include }"; + // Find and cache std::string. + if (S.StdNamespace && !StdString) + StdString = LookupNamed(S, "string", S.getStdNamespace()); + if (StdString) { + const auto *StdStringTD = llvm::dyn_cast(StdString); + RetTy = QualType(StdStringTD->getTypeForDecl(), /*Quals=*/0); + } + } + + // Create the wrapper function. + DeclarationName DeclName = &Ctx.Idents.get(CreateUniqName(WrapperName)); + QualType FnTy = + Ctx.getFunctionType(RetTy, {}, FunctionProtoType::ExtProtoInfo()); + auto *WrapperFD = FunctionDecl::Create( + Ctx, Ctx.getTranslationUnitDecl(), SourceLocation(), SourceLocation(), + DeclName, FnTy, Ctx.getTrivialTypeSourceInfo(FnTy), SC_None); + + void *ValPtr = V.getPtr(); + + // FIXME: We still need to understand why we have to get the pointer to the + // underlying Value storage for this to work reliabily... + // if (!V.isManuallyAlloc()) + // ValPtr = V.getPtrAddress(); + + if (llvm::Error E = + BuildWrapperBody(R, S, Ctx, WrapperFD, V.getType(), ValPtr)) { + llvm::logAllUnhandledErrors(std::move(E), llvm::errs(), + "Fail to build a wrapper function"); + return "{error: unable to print the value}"; + } + + auto AddrOrErr = CompileDecl(*this, WrapperFD); + if (!AddrOrErr) + llvm::logAllUnhandledErrors(AddrOrErr.takeError(), llvm::errs(), + "Fail to get symbol address"); + if (StdString) { + if (auto *Main = AddrOrErr->toPtr()) + return (*Main)(); + } else { + if (auto *Main = AddrOrErr->toPtr()) + return (*Main)(); + } + return "{Unable to print the value!}"; +} + +struct ValueRef: public Value { + ValueRef(Interpreter *In, void *Ty) : Value(In, Ty) { + // Tell the base class to not try to deallocate if it manages the value. + IsManuallyAlloc = false; + } + void setData(long double D) { Data.m_LongDouble = D; } +}; + +std::string Interpreter::ValueDataToString(const Value &V) { + Sema &S = getCompilerInstance()->getSema(); + ASTContext &Ctx = S.getASTContext(); + + QualType QT = V.getType(); + + while (const ConstantArrayType *CAT = Ctx.getAsConstantArrayType(QT)) { + std::string result = "{ "; + for (unsigned Idx = 0, N = CAT->getZExtSize(); Idx < N; ++Idx) { + QualType ElemTy = CAT->getElementType(); + ValueRef InnerV = ValueRef(this, ElemTy.getAsOpaquePtr()); + const Type* BaseTy = CAT->getBaseElementTypeUnsafe(); + if (ElemTy->isBuiltinType()) { + // Arrays model builtin types as a pointer to the builtin. We should + // build the dereference to the element type if it is stored as a + // builtin. + InnerV.setData(V.convertTo()); + } else { + uintptr_t offset = (uintptr_t)V.getPtr() + Idx * Ctx.getTypeSize(BaseTy) / 8; + InnerV.setPtr((void*)offset); + } + result += ValueDataToString(InnerV); + if (Idx < N - 1) + result += ", "; + } + result+=" }"; + return result; + } + + QualType DesugaredTy = QT.getDesugaredType(Ctx); + QualType NonRefTy = DesugaredTy.getNonReferenceType(); + + LookupResult R = LookupUserDefined(S, QT); + if (!R.empty()) + return CallUserSpecifiedPrinter(R, V); + + // If it is a builtin type dispatch to the builtin overloads. + if (auto *BT = DesugaredTy.getCanonicalType()->getAs()) { + std::string str; + llvm::raw_string_ostream ss(str); + switch (BT->getKind()) { + default: + return "{ error: unknown builtin type '" + std::to_string(BT->getKind()) + + " '}"; + case clang::BuiltinType::Bool: + ss << ((V.getBool()) ? "true" : "false"); + return str; + case clang::BuiltinType::Char_S: + ss << V.getChar_S(); + return str; + case clang::BuiltinType::SChar: + ss << V.getSChar(); + return str; + case clang::BuiltinType::Char_U: + ss << V.getChar_U(); + return str; + case clang::BuiltinType::UChar: + ss << V.getUChar(); + return str; + case clang::BuiltinType::Short: + ss << V.getShort(); + return str; + case clang::BuiltinType::UShort: + ss << V.getUShort(); + return str; + case clang::BuiltinType::Int: + ss << V.getInt(); + return str; + case clang::BuiltinType::UInt: + ss << V.getUInt(); + return str; + case clang::BuiltinType::Long: + ss << V.getLong(); + return str; + case clang::BuiltinType::ULong: + ss << V.getULong(); + return str; + case clang::BuiltinType::LongLong: + ss << V.getLongLong(); + return str; + case clang::BuiltinType::ULongLong: + ss << V.getULongLong(); + return str; + case clang::BuiltinType::Float: + ss << llvm::format("%#.6g", V.getFloat()) << 'f'; + return str; + case clang::BuiltinType::Double: + ss << llvm::format("%#.12g", V.getDouble()); + return str; + case clang::BuiltinType::LongDouble: + ss << llvm::format("%#.8Lg", V.getLongDouble()) << 'L'; + return str; + } + } + + if ((NonRefTy->isPointerType() || NonRefTy->isMemberPointerType()) && + NonRefTy->getPointeeType()->isFunctionProtoType()) + return FunctionToString(V, V.getPtr()); + + if (NonRefTy->isFunctionType()) + return FunctionToString(V, &V); + + if (NonRefTy->isEnumeralType()) + return EnumToString(V); + + if (NonRefTy->isNullPtrType()) + return "nullptr\n"; + + // FIXME: Add support for custom printers in C. + if (NonRefTy->isPointerType()) { + if (NonRefTy->getPointeeType()->isCharType()) + return AddressToString((const char **)V.getPtrAddress(), '@'); + } + + // Fall back to printing just the address of the unknown object. + return AddressToString(V.getPtr(), '@'); +} + +std::string Interpreter::ValueTypeToString(const Value &V) const { + ASTContext &Ctx = const_cast(V.getASTContext()); + QualType QT = V.getType(); + + std::string QTStr = QualTypeToString(Ctx, QT); + + if (QT->isReferenceType()) + QTStr += " &"; + + return QTStr; +} + +llvm::Expected +Interpreter::CompileDtorCall(CXXRecordDecl *CXXRD) { + assert(CXXRD && "Cannot compile a destructor for a nullptr"); + if (auto Dtor = Dtors.find(CXXRD); Dtor != Dtors.end()) + return Dtor->getSecond(); + + if (CXXRD->hasIrrelevantDestructor()) + return llvm::orc::ExecutorAddr{}; + + CXXDestructorDecl *DtorRD = + getCompilerInstance()->getSema().LookupDestructor(CXXRD); + + llvm::StringRef Name = + getCodeGen()->GetMangledName(GlobalDecl(DtorRD, Dtor_Base)); + auto AddrOrErr = getSymbolAddress(Name); + if (!AddrOrErr) + return AddrOrErr.takeError(); + + Dtors[CXXRD] = *AddrOrErr; + return AddrOrErr; +} + +enum InterfaceKind { NoAlloc, WithAlloc, CopyArray, NewTag }; + +class InterfaceKindVisitor + : public TypeVisitor { + + Sema &S; + Expr *E; + llvm::SmallVectorImpl &Args; + +public: + InterfaceKindVisitor(Sema &S, Expr *E, llvm::SmallVectorImpl &Args) + : S(S), E(E), Args(Args) {} + + InterfaceKind computeInterfaceKind(QualType Ty) { + return Visit(Ty.getTypePtr()); + } + + InterfaceKind VisitRecordType(const RecordType *Ty) { + if (S.getLangOpts().CPlusPlus) + return InterfaceKind::WithAlloc; + ExprResult AddrOfE = S.CreateBuiltinUnaryOp(SourceLocation(), UO_AddrOf, + E->IgnoreImpCasts()); + assert(!AddrOfE.isInvalid() && "Can not create unary expression"); + Args.push_back(AddrOfE.get()); + return InterfaceKind::NoAlloc; + } + + InterfaceKind VisitMemberPointerType(const MemberPointerType *Ty) { + return InterfaceKind::WithAlloc; + } + + InterfaceKind VisitConstantArrayType(const ConstantArrayType *Ty) { + return InterfaceKind::CopyArray; + } + + InterfaceKind VisitFunctionProtoType(const FunctionProtoType *Ty) { + HandlePtrType(Ty); + return InterfaceKind::NoAlloc; + } + + InterfaceKind VisitPointerType(const PointerType *Ty) { + HandlePtrType(Ty); + return InterfaceKind::NoAlloc; + } + + InterfaceKind VisitReferenceType(const ReferenceType *Ty) { + ExprResult AddrOfE = S.CreateBuiltinUnaryOp(SourceLocation(), UO_AddrOf, E); + assert(!AddrOfE.isInvalid() && "Can not create unary expression"); + Args.push_back(AddrOfE.get()); + return InterfaceKind::NoAlloc; + } + + InterfaceKind VisitBuiltinType(const BuiltinType *Ty) { + if (Ty->isNullPtrType()) + Args.push_back(E); + else if (Ty->isFloatingType()) + Args.push_back(E); + else if (Ty->isIntegralOrEnumerationType()) + HandleIntegralOrEnumType(Ty); + else if (Ty->isVoidType()) { + // Do we need to still run `E`? + } + + return InterfaceKind::NoAlloc; + } + + InterfaceKind VisitEnumType(const EnumType *Ty) { + HandleIntegralOrEnumType(Ty); + return InterfaceKind::NoAlloc; + } + +private: + // Force cast these types to the uint that fits the register size. That way we + // reduce the number of overloads of `__clang_Interpreter_SetValueNoAlloc`. + void HandleIntegralOrEnumType(const Type *Ty) { + ASTContext &Ctx = S.getASTContext(); + uint64_t PtrBits = Ctx.getTypeSize(Ctx.VoidPtrTy); + QualType UIntTy = Ctx.getBitIntType(/*Unsigned=*/true, PtrBits); + TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(UIntTy); + ExprResult CastedExpr = + S.BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), E); + assert(!CastedExpr.isInvalid() && "Cannot create cstyle cast expr"); + Args.push_back(CastedExpr.get()); + } + + void HandlePtrType(const Type *Ty) { + ASTContext &Ctx = S.getASTContext(); + TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ctx.VoidPtrTy); + ExprResult CastedExpr = + S.BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), E); + assert(!CastedExpr.isInvalid() && "Can not create cstyle cast expression"); + Args.push_back(CastedExpr.get()); + } +}; + +static constexpr llvm::StringRef VPName[] = { + "__clang_Interpreter_SetValueNoAlloc", + "__clang_Interpreter_SetValueWithAlloc", + "__clang_Interpreter_SetValueCopyArr", "__ci_newtag"}; + +// This synthesizes a call expression to a speciall +// function that is responsible for generating the Value. +// In general, we transform c++: +// clang-repl> x +// To: +// // 1. If x is a built-in type like int, float. +// __clang_Interpreter_SetValueNoAlloc(ThisInterp, OpaqueValue, xQualType, x); +// // 2. If x is a struct, and a lvalue. +// __clang_Interpreter_SetValueNoAlloc(ThisInterp, OpaqueValue, xQualType, +// &x); +// // 3. If x is a struct, but a rvalue. +// new (__clang_Interpreter_SetValueWithAlloc(ThisInterp, OpaqueValue, +// xQualType)) (x); +llvm::Expected Interpreter::convertExprToValue(Expr *E) { + Sema &S = getCompilerInstance()->getSema(); + ASTContext &Ctx = S.getASTContext(); + + // Find the value printing builtins. + if (!ValuePrintingInfo[0]) { + assert(llvm::all_of(ValuePrintingInfo, [](Expr *E) { return !E; })); + + auto LookupInterface = [&](Expr *&Interface, + llvm::StringRef Name) -> llvm::Error { + LookupResult R(S, &Ctx.Idents.get(Name), SourceLocation(), + Sema::LookupOrdinaryName, + RedeclarationKind::ForVisibleRedeclaration); + S.LookupQualifiedName(R, Ctx.getTranslationUnitDecl()); + if (R.empty()) + return llvm::make_error( + Name + " not found!", llvm::inconvertibleErrorCode()); + + CXXScopeSpec CSS; + Interface = S.BuildDeclarationNameExpr(CSS, R, /*ADL=*/false).get(); + return llvm::Error::success(); + }; + if (llvm::Error Err = + LookupInterface(ValuePrintingInfo[NoAlloc], VPName[NoAlloc])) + return std::move(Err); + + if (Ctx.getLangOpts().CPlusPlus) { + if (llvm::Error Err = + LookupInterface(ValuePrintingInfo[WithAlloc], VPName[WithAlloc])) + return std::move(Err); + if (llvm::Error Err = + LookupInterface(ValuePrintingInfo[CopyArray], VPName[CopyArray])) + return std::move(Err); + if (llvm::Error Err = + LookupInterface(ValuePrintingInfo[NewTag], VPName[NewTag])) + return std::move(Err); + } + } + + llvm::SmallVector AdjustedArgs; + // Create parameter `ThisInterp`. + AdjustedArgs.push_back(CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)this)); + + // Create parameter `OutVal`. + AdjustedArgs.push_back( + CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)&LastValue)); + + // Build `__clang_Interpreter_SetValue*` call. + + // Get rid of ExprWithCleanups. + if (auto *EWC = llvm::dyn_cast_if_present(E)) + E = EWC->getSubExpr(); + + QualType Ty = E->getType(); + QualType DesugaredTy = Ty.getDesugaredType(Ctx); + + // For lvalue struct, we treat it as a reference. + if (DesugaredTy->isRecordType() && E->isLValue()) { + DesugaredTy = Ctx.getLValueReferenceType(DesugaredTy); + Ty = Ctx.getLValueReferenceType(Ty); + } + + Expr *TypeArg = + CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)Ty.getAsOpaquePtr()); + // The QualType parameter `OpaqueType`, represented as `void*`. + AdjustedArgs.push_back(TypeArg); + + // We push the last parameter based on the type of the Expr. Note we need + // special care for rvalue struct. + InterfaceKindVisitor V(S, E, AdjustedArgs); + Scope *Scope = nullptr; + ExprResult SetValueE; + InterfaceKind Kind = V.computeInterfaceKind(DesugaredTy); + if (!Ctx.getLangOpts().CPlusPlus && Kind == InterfaceKind::WithAlloc) + Kind = InterfaceKind::NoAlloc; + switch (Kind) { + case InterfaceKind::WithAlloc: + LLVM_FALLTHROUGH; + case InterfaceKind::CopyArray: { + // __clang_Interpreter_SetValueWithAlloc. + ExprResult AllocCall = + S.ActOnCallExpr(Scope, ValuePrintingInfo[InterfaceKind::WithAlloc], + E->getBeginLoc(), AdjustedArgs, E->getEndLoc()); + if (AllocCall.isInvalid()) + return llvm::make_error( + "Cannot call to " + VPName[WithAlloc], + llvm::inconvertibleErrorCode()); + + TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ty, SourceLocation()); + + // Force CodeGen to emit destructor. + if (auto *RD = Ty->getAsCXXRecordDecl()) { + auto *Dtor = S.LookupDestructor(RD); + Dtor->addAttr(UsedAttr::CreateImplicit(Ctx)); + getCompilerInstance()->getASTConsumer().HandleTopLevelDecl( + DeclGroupRef(Dtor)); + } + + // __clang_Interpreter_SetValueCopyArr. + if (Kind == InterfaceKind::CopyArray) { + const auto *ConstantArrTy = + cast(DesugaredTy.getTypePtr()); + size_t ArrSize = Ctx.getConstantArrayElementCount(ConstantArrTy); + Expr *ArrSizeExpr = IntegerLiteralExpr(Ctx, ArrSize); + Expr *Args[] = {E, AllocCall.get(), ArrSizeExpr}; + SetValueE = + S.ActOnCallExpr(Scope, ValuePrintingInfo[InterfaceKind::CopyArray], + SourceLocation(), Args, SourceLocation()); + if (SetValueE.isInvalid()) + return llvm::make_error( + "Cannot call to " + VPName[CopyArray], + llvm::inconvertibleErrorCode()); + break; + // return SetValueE.get(); + } + Expr *Args[] = {AllocCall.get(), ValuePrintingInfo[InterfaceKind::NewTag]}; + ExprResult CXXNewCall = S.BuildCXXNew( + E->getSourceRange(), + /*UseGlobal=*/true, /*PlacementLParen=*/SourceLocation(), Args, + /*PlacementRParen=*/SourceLocation(), + /*TypeIdParens=*/SourceRange(), TSI->getType(), TSI, std::nullopt, + E->getSourceRange(), E); + + if (CXXNewCall.isInvalid()) + return llvm::make_error( + "Cannot build a call to placement new", + llvm::inconvertibleErrorCode()); + + SetValueE = S.ActOnFinishFullExpr(CXXNewCall.get(), + /*DiscardedValue=*/false); + break; + } + // __clang_Interpreter_SetValueNoAlloc. + case InterfaceKind::NoAlloc: { + SetValueE = + S.ActOnCallExpr(Scope, ValuePrintingInfo[InterfaceKind::NoAlloc], + E->getBeginLoc(), AdjustedArgs, E->getEndLoc()); + break; + } + default: + llvm_unreachable("Unhandled InterfaceKind"); + } + + // It could fail, like printing an array type in C. (not supported) + if (SetValueE.isInvalid()) + return E; + + return SetValueE.get(); +} + +} // namespace clang + +// Temporary rvalue struct that need special care. +REPL_EXTERNAL_VISIBILITY void * +__clang_Interpreter_SetValueWithAlloc(void *This, void *OutVal, + void *OpaqueType) { + Value &VRef = *(Value *)OutVal; + VRef = Value(static_cast(This), OpaqueType); + return VRef.getPtr(); +} + +extern "C" void REPL_EXTERNAL_VISIBILITY __clang_Interpreter_SetValueNoAlloc( + void *This, void *OutVal, void *OpaqueType, ...) { + Value &VRef = *(Value *)OutVal; + Interpreter *I = static_cast(This); + VRef = Value(I, OpaqueType); + if (VRef.isVoid()) + return; + + va_list args; + va_start(args, /*last named param*/ OpaqueType); + + QualType QT = VRef.getType(); + if (VRef.getKind() == Value::K_PtrOrObj) { + VRef.setPtr(va_arg(args, void *)); + } else { + if (const auto *ET = QT->getAs()) + QT = ET->getDecl()->getIntegerType(); + switch (QT->castAs()->getKind()) { + default: + llvm_unreachable("unknown type kind!"); + break; + // Types shorter than int are resolved as int, else va_arg has UB. + case BuiltinType::Bool: + VRef.setBool(va_arg(args, int)); + break; + case BuiltinType::Char_S: + VRef.setChar_S(va_arg(args, int)); + break; + case BuiltinType::SChar: + VRef.setSChar(va_arg(args, int)); + break; + case BuiltinType::Char_U: + VRef.setChar_U(va_arg(args, unsigned)); + break; + case BuiltinType::UChar: + VRef.setUChar(va_arg(args, unsigned)); + break; + case BuiltinType::Short: + VRef.setShort(va_arg(args, int)); + break; + case BuiltinType::UShort: + VRef.setUShort(va_arg(args, unsigned)); + break; + case BuiltinType::Int: + VRef.setInt(va_arg(args, int)); + break; + case BuiltinType::UInt: + VRef.setUInt(va_arg(args, unsigned)); + break; + case BuiltinType::Long: + VRef.setLong(va_arg(args, long)); + break; + case BuiltinType::ULong: + VRef.setULong(va_arg(args, unsigned long)); + break; + case BuiltinType::LongLong: + VRef.setLongLong(va_arg(args, long long)); + break; + case BuiltinType::ULongLong: + VRef.setULongLong(va_arg(args, unsigned long long)); + break; + // Types shorter than double are resolved as double, else va_arg has UB. + case BuiltinType::Float: + VRef.setFloat(va_arg(args, double)); + break; + case BuiltinType::Double: + VRef.setDouble(va_arg(args, double)); + break; + case BuiltinType::LongDouble: + VRef.setLongDouble(va_arg(args, long double)); + break; + // See REPL_BUILTIN_TYPES. + } + } + va_end(args); +} + +// A trampoline to work around the fact that operator placement new cannot +// really be forward declared due to libc++ and libstdc++ declaration mismatch. +// FIXME: __clang_Interpreter_NewTag is ODR violation because we get the same +// definition in the interpreter runtime. We should move it in a runtime header +// which gets included by the interpreter and here. +struct __clang_Interpreter_NewTag {}; +REPL_EXTERNAL_VISIBILITY void * +operator new(size_t __sz, void *__p, __clang_Interpreter_NewTag) noexcept { + // Just forward to the standard operator placement new. + return operator new(__sz, __p); +} diff --git a/clang/lib/Interpreter/Value.cpp b/clang/lib/Interpreter/Value.cpp index eb2ce9c9fd330..0de66b4f0e699 100644 --- a/clang/lib/Interpreter/Value.cpp +++ b/clang/lib/Interpreter/Value.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "clang/Interpreter/Value.h" +#include "InterpreterUtils.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Type.h" #include "clang/Interpreter/Interpreter.h" @@ -22,6 +23,8 @@ #include #include +using namespace clang; + namespace { // This is internal buffer maintained by Value, used to hold temporaries. @@ -231,6 +234,11 @@ void *Value::getPtr() const { return Data.m_Ptr; } +void **Value::getPtrAddress() const { + assert(ValueKind == K_PtrOrObj); + return &const_cast(this)->Data.m_Ptr; +} + QualType Value::getType() const { return QualType::getFromOpaquePtr(OpaqueType); } @@ -253,17 +261,35 @@ const ASTContext &Value::getASTContext() const { return getInterpreter().getASTContext(); } -void Value::dump() const { print(llvm::outs()); } +void Value::dump() { print(llvm::outs()); } void Value::printType(llvm::raw_ostream &Out) const { - Out << "Not implement yet.\n"; + Out << Interp->ValueTypeToString(*this); } -void Value::printData(llvm::raw_ostream &Out) const { - Out << "Not implement yet.\n"; + +void Value::printData(llvm::raw_ostream &Out) { + Out << Interp->ValueDataToString(*this); } -void Value::print(llvm::raw_ostream &Out) const { +// FIXME: We do not support the multiple inheritance case where one of the base +// classes has a pretty-printer and the other does not. +void Value::print(llvm::raw_ostream &Out) { assert(OpaqueType != nullptr && "Can't print default Value"); - Out << "Not implement yet.\n"; + + // Don't even try to print a void or an invalid type, it doesn't make sense. + if (getType()->isVoidType() || !isValid()) + return; + + // We need to get all the results together then print it, since `printType` is + // much faster than `printData`. + std::string Str; + llvm::raw_string_ostream SS(Str); + + SS << "("; + printType(SS); + SS << ") "; + printData(SS); + SS << "\n"; + Out << Str; } } // namespace clang diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp index bdb3fc051d0b3..56b1141f02a46 100644 --- a/clang/lib/Parse/ParseStmt.cpp +++ b/clang/lib/Parse/ParseStmt.cpp @@ -583,7 +583,8 @@ StmtResult Parser::ParseExprStatement(ParsedStmtContext StmtCtx) { } Token *CurTok = nullptr; - // Note we shouldn't eat the token since the callback needs it. + // If the semicolon is missing at the end of REPL input, we want to print + // the result. Note we shouldn't eat the token since the callback needs it. if (Tok.is(tok::annot_repl_input_end)) CurTok = &Tok; else diff --git a/clang/test/Interpreter/pretty-print.c b/clang/test/Interpreter/pretty-print.c index d21749a649e1c..63e63631640a3 100644 --- a/clang/test/Interpreter/pretty-print.c +++ b/clang/test/Interpreter/pretty-print.c @@ -3,9 +3,70 @@ // RUN: cat %s | clang-repl -Xcc -xc | FileCheck %s // RUN: cat %s | clang-repl -Xcc -std=c++11 | FileCheck %s -// Fails with `Symbols not found: [ __clang_Interpreter_SetValueNoAlloc ]`. // UNSUPPORTED: hwasan const char* c_str = "Hello, world!"; c_str -// CHECK: Not implement yet. +char c = 'a'; c +// CHECK: (char) 'a' + +c_str = "Goodbye, world!"; c_str +// CHECK-NEXT: (const char *) "Goodbye, world!" + +const char* c_null_str = 0; c_null_str +// CHECK-NEXT: (const char *) nullptr + +"Hello, world" +// CHECK-NEXT: (const char[13]) "Hello, world" + +int x = 42; x +// CHECK-NEXT: (int) 42 + +&x +// CHECK-NEXT: (int *) @0x{{[0-9a-f]+}} + +x - 2 +// CHECK-NEXT: (int) 40 + +float f = 4.2f; f +// CHECK-NEXT: (float) 4.20000f + +double d = 4.21; d +// CHECK-NEXT: (double) 4.21000000000 + +struct S1{} s1; s1 +// CHECK-NEXT: (S1 &) @0x{{[0-9a-f]+}} + +struct S2 {int d;} E = {22}; E +// CHECK-NEXT: (struct S2 &) @0x{{[0-9a-f]+}} +E.d +// CHECK-NEXT: (int) 22 + +// Arrays. + +int arr[3] = {1,2,3}; arr +// CHECK-NEXT: (int[3]) { 1, 2, 3 } + +int foo() { return 42; } foo() +// CHECK-NEXT: (int) 42 + +void bar() {} bar() + +struct ConstLiteral{}; +const char * caas__runtime__PrintValueRuntime(const struct ConstLiteral *) { \ + return "ConstLiteral"; \ +} +struct ConstLiteral CL; CL +// CHECK-NEXT: ConstLiteral + +struct Point{int x; int y;}; +const char * caas__runtime__PrintValueRuntime(const struct Point *p) { \ + char[11 + 11 + 4 + 1] result; \ + sprintf(result, "(%d, %d)", p->x, p->y); \ + return strdup(result); \ +} + +Point P {1,2}; P +// CHECK-NEXT: (1, 2) + +%quit diff --git a/clang/test/Interpreter/pretty-print.cpp b/clang/test/Interpreter/pretty-print.cpp new file mode 100644 index 0000000000000..6e15a8855db42 --- /dev/null +++ b/clang/test/Interpreter/pretty-print.cpp @@ -0,0 +1,174 @@ +// RUN: clang-repl "int i = 10;" 'extern "C" int printf(const char*,...);' \ +// RUN: 'auto r1 = printf("i = %d\n", i);' | FileCheck --check-prefix=CHECK-DRIVER %s +// UNSUPPORTED: system-aix +// CHECK-DRIVER: i = 10 +// RUN: cat %s | clang-repl -Xcc -std=c++11 -Xcc -fno-delayed-template-parsing | FileCheck %s +extern "C" int printf(const char*,...); + +struct NonPOD { \ + static int sI; \ + int I; \ + NonPOD(): I(sI++) {} \ +}; +namespace caas { namespace runtime { \ + const char* to_string(const NonPOD* type) { \ + switch (type->I) { \ + default: return "out-of-bounds"; \ + case 0: return "0"; case 1: return "1"; case 2: return "2"; \ + case 3: return "3"; case 4: return "4"; case 5: return "5"; \ + } \ +}}} + +int NonPOD::sI = 0; + +NonPOD non_pod_arr[2][3]; +// Check array order after the value printing transformation. Also make sure we +// can handle the forward declaration of operator new with placement. +non_pod_arr +// CHECK: (NonPOD[2][3]) { { 0, 1, 2 }, { 3, 4, 5 } } + +char ch_arr[2][3][1] = {{{'a'}, {'b'}, {'c'}}, {{'d'}, {'e'}, {'f'}}}; +ch_arr +// CHECK: (char[2][3][1]) {{{'a'}, {'b'}, {'c'}}, {{'d'}, {'e'}, {'f'}}} + +struct S3 { int* p; S3() { p = new int(42); } ~S3() { delete p; } }; +S3{} +// CHECK-NEXT: (S3) @0x{{[0-9a-f]+}} +S3 s3; +s3 +// CHECK-NEXT: (S3 &) @0x{{[0-9a-f]+}} + +struct S4 { ~S4() { printf("~S4()\n"); }}; +S4{} +// CHECK-NEXT: (S4) @0x{{[0-9a-f]+}} + +enum Enum{ e1 = -12, e2, e3=33, e4, e5 = 33}; +e2 +// CHECK-NEXT: (Enum) (e2) : int -11 +::e1 +// CHECK-NEXT: (Enum) (e1) : int -12 + +enum class Color { Black = 0, Red, Green }; +Color::Black +// CHECK-NEXT: (Color) (Color::Black) : int 0 + + +// Lambdas. + +auto Lambda1 = []{}; +Lambda1 +// CHECK-NEXT: ((lambda) &) @0x{{[0-9a-f]+}} +[]{} +// CHECK-NEXT: ((lambda at input_line_{{[0-9]+}}:1:1)) @0x{{[0-9a-f]+}} + +template struct F{ enum {RET=F::RET*n} ; }; +template<> struct F<0> { enum {RET = 1}; }; +F<7>::RET +// CHECK-NEXT: (F<7>::(unnamed enum at input_line_{{[0-9]+}}:1:27)) (F<7>::RET) : unsigned int 5040 + +struct S5 { int foo() { return 42; }}; +&S5::foo +// CHECK-NEXT: (int (S5::*)()) Function @0x{{[0-9a-f]+}} + +#include + +auto p1 = std::make_shared(42); +p1 +// CHECK-NEXT: (std::shared_ptr &) std::shared_ptr -> @0x{{[0-9a-f]+}} + +std::unique_ptr p2(new int(42)); +p2 +// CHECK-NEXT: (std::unique_ptr &) std::unique_ptr -> @0x{{[0-9a-f]+}} + +#include +std::array a{1, 2, 3}; +a +// CHECK-NEXT: (std::array &) { 1, 2, 3 } + +#include +std::vector v1 = {7, 5, 16, 8}; +v1 +// CHECK-NEXT: (std::vector &) { 7, 5, 16, 8 } + +std::vector v = {true, false, true}; +v +// CHECK-NEXT: (std::vector &) { true, false, true } + +#include +std::deque dq = {7, 5, 16, 8}; +dq +// CHECK-NEXT: (std::deque &) { 7, 5, 16, 8 } + +#include +std::forward_list fl {3,4,5,6}; +fl +// CHECK-NEXT: (std::forward_list &) { 3, 4, 5, 6 } + +#include +std::set z1 = {2,4,6,8}; +z1 +// CHECK-NEXT: (std::set &) { 2, 4, 6, 8 } + +#include +std::unordered_set z2 = {8,2,4,6}; +z2 +// CHECK-NEXT: (std::unordered_set &) { [[Num:[0-9]+]], [[Num:[0-9]+]], [[Num:[0-9]+]], [[Num:[0-9]+]] } + +std::multiset e {3,2,1,2,4,7,3}; +e +// CHECK-NEXT: (std::multiset &) { 1, 2, 2, 3, 3, 4, 7 } + +#include +std::string std_str = "Hello, world!"; +std_str +// CHECK-NEXT: (std::string &) "Hello, world!" + +#include +std::pair pr(42,'a'); +pr +// CHECK-NEXT: (std::pair &) { 42, 'a' } + +#include +std::tuple tu(42,3.14,'a'); +tu +// CHECK-NEXT: (std::tuple &) { 42, 3.14000000000, 'a' } + +#include +std::map m1{{"CPU", 10}, {"GPU", 15}, {"RAM", 20}}; +m1 +// CHECK-NEXT: (std::map &) { "CPU" => 10, "GPU" => 15, "RAM" => 20 } + +#include +std::unordered_map m2 = { {1,2}, {3,4}}; +m2 +// CHECK-NEXT: (std::unordered_map &) { [[Num:[0-9]+]] => [[Num:[0-9]+]], [[Num:[0-9]+]] => [[Num:[0-9]+]] } + +struct MyDate { \ + unsigned year; \ + char month; \ + char day; \ +}; + +#include + +namespace caas { namespace runtime { \ + std::string to_string(const MyDate* d) { \ + std::string result; \ + result = std::to_string(d->year) + '-'; \ + switch(d->month) { default: result += "invalid month"; break; \ + case 1: result += "Jan"; break; case 2: result += "Feb"; break; \ + case 3: result += "Mar"; break; case 4: result += "Apr"; break; \ + case 5: result += "May"; break; case 6: result += "Jun"; break; \ + case 7: result += "Jul"; break; case 8: result += "Aug"; break; \ + case 9: result += "Sep"; break; case 10: result += "Oct"; break; \ + case 11: result += "Nov"; break; case 12: result += "Dec"; break; \ + } \ + result += '-'; \ + result += std::to_string(d->day); \ + return result; \ + } \ + }} +MyDate{2024, 8, 23} +// CHECK-NEXT: (MyDate) 2024-Aug-23 +%quit + diff --git a/clang/tools/clang-repl/CMakeLists.txt b/clang/tools/clang-repl/CMakeLists.txt index a35ff13494e11..0b09bbc74e581 100644 --- a/clang/tools/clang-repl/CMakeLists.txt +++ b/clang/tools/clang-repl/CMakeLists.txt @@ -33,6 +33,14 @@ if(MSVC) ??_U@YAPEAX_K@Z ??_V@YAXPEAX@Z ??3@YAXPEAX_K@Z + ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV01@H@Z + ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV01@M@Z + ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV01@N@Z + ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV01@PEBX@Z + ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV01@P6AAEAV01@AEAV01@@Z@Z + ??$?6U?$char_traits@D@std@@@std@@YAAEAV?$basic_ostream@DU?$char_traits@D@std@@@0@AEAV10@D@Z + ??$?6U?$char_traits@D@std@@@std@@YAAEAV?$basic_ostream@DU?$char_traits@D@std@@@0@AEAV10@PEBD@Z + ?_Facet_Register@std@@YAXPEAV_Facet_base@1@@Z ) else() set(clang_repl_exports ${clang_repl_exports} @@ -42,6 +50,14 @@ if(MSVC) ??_U@YAPAXI@Z ??_V@YAXPAX@Z ??_V@YAXPAXI@Z + ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@H@Z + ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@M@Z + ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@N@Z + ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@PBX@Z + ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@P6AAAV01@AAV01@@Z@Z + ??$?6U?$char_traits@D@std@@@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@0@AAV10@D@Z + ??$?6U?$char_traits@D@std@@@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@0@AAV10@PBD@Z + ?_Facet_Register@std@@YAXPAV_Facet_base@1@@Z ) endif() diff --git a/clang/tools/clang-repl/ClangRepl.cpp b/clang/tools/clang-repl/ClangRepl.cpp index 9cfc70462893d..fcdfdd2b475bf 100644 --- a/clang/tools/clang-repl/ClangRepl.cpp +++ b/clang/tools/clang-repl/ClangRepl.cpp @@ -201,7 +201,6 @@ int main(int argc, const char **argv) { DeviceCI->LoadRequestedPlugins(); std::unique_ptr Interp; - if (CudaEnabled) { Interp = ExitOnErr( clang::Interpreter::createWithCUDA(std::move(CI), std::move(DeviceCI))); @@ -232,8 +231,8 @@ int main(int argc, const char **argv) { llvm::StringRef L = *Line; L = L.trim(); if (L.ends_with("\\")) { - // FIXME: Support #ifdef X \ ... Input += L.drop_back(1); + Input += "\n"; LE.setPrompt("clang-repl... "); continue; } diff --git a/clang/unittests/Interpreter/CodeCompletionTest.cpp b/clang/unittests/Interpreter/CodeCompletionTest.cpp index 72fcce76a1029..23cfc469695d2 100644 --- a/clang/unittests/Interpreter/CodeCompletionTest.cpp +++ b/clang/unittests/Interpreter/CodeCompletionTest.cpp @@ -26,7 +26,7 @@ auto CB = clang::IncrementalCompilerBuilder(); class CodeCompletionTest : public InterpreterTestBase { public: - std::unique_ptr Interp; + std::unique_ptr Interp; void SetUp() override { if (!HostSupportsJIT()) diff --git a/clang/unittests/Interpreter/InterpreterExtensionsTest.cpp b/clang/unittests/Interpreter/InterpreterExtensionsTest.cpp index 5f1f29cebab14..85993e3e73e2c 100644 --- a/clang/unittests/Interpreter/InterpreterExtensionsTest.cpp +++ b/clang/unittests/Interpreter/InterpreterExtensionsTest.cpp @@ -65,41 +65,13 @@ class InterpreterExtensionsTest : public InterpreterTestBase { } }; -class RecordRuntimeIBMetrics : public Interpreter { - struct NoopRuntimeInterfaceBuilder : public RuntimeInterfaceBuilder { - NoopRuntimeInterfaceBuilder(Sema &S) : S(S) {} - - TransformExprFunction *getPrintValueTransformer() override { - TransformerQueries += 1; - return &noop; - } - - static ExprResult noop(RuntimeInterfaceBuilder *Builder, Expr *E, - ArrayRef FixedArgs) { - auto *B = static_cast(Builder); - B->TransformedExprs += 1; - return B->S.ActOnFinishFullExpr(E, /*DiscardedValue=*/false); - } - - Sema &S; - size_t TransformedExprs = 0; - size_t TransformerQueries = 0; - }; - -public: - // Inherit with using wouldn't make it public - RecordRuntimeIBMetrics(std::unique_ptr CI, llvm::Error &Err) - : Interpreter(std::move(CI), Err) {} - - std::unique_ptr FindRuntimeInterface() override { - assert(RuntimeIBPtr == nullptr && "We create the builder only once"); - Sema &S = getCompilerInstance()->getSema(); - auto RuntimeIB = std::make_unique(S); - RuntimeIBPtr = RuntimeIB.get(); - return RuntimeIB; - } - - NoopRuntimeInterfaceBuilder *RuntimeIBPtr = nullptr; +struct OutOfProcInterpreter : public Interpreter { + OutOfProcInterpreter( + std::unique_ptr CI, llvm::Error &ErrOut, + std::unique_ptr Consumer, + std::unique_ptr JITBuilder = nullptr) + : Interpreter(std::move(CI), ErrOut, std::move(JITBuilder), + std::move(Consumer)) {} }; TEST_F(InterpreterExtensionsTest, FindRuntimeInterface) { @@ -108,13 +80,23 @@ TEST_F(InterpreterExtensionsTest, FindRuntimeInterface) { clang::IncrementalCompilerBuilder CB; llvm::Error ErrOut = llvm::Error::success(); - RecordRuntimeIBMetrics Interp(cantFail(CB.CreateCpp()), ErrOut); + auto CI = cantFail(CB.CreateCpp()); + // Do not attach the default consumer which is specialized for in-process. + class NoopConsumer : public ASTConsumer {}; + std::unique_ptr C = std::make_unique(); + OutOfProcInterpreter I(std::move(CI), ErrOut, std::move(C), + /*JITBuilder=*/nullptr); cantFail(std::move(ErrOut)); - cantFail(Interp.Parse("int a = 1; a")); - cantFail(Interp.Parse("int b = 2; b")); - cantFail(Interp.Parse("int c = 3; c")); - EXPECT_EQ(3U, Interp.RuntimeIBPtr->TransformedExprs); - EXPECT_EQ(1U, Interp.RuntimeIBPtr->TransformerQueries); + cantFail(I.Parse("int a = 1; a")); + cantFail(I.Parse("int b = 2; b")); + cantFail(I.Parse("int c = 3; c")); + + // Make sure no clang::Value logic is attached by the Interpreter. + Value V1; + llvm::cantFail(I.ParseAndExecute("int x = 42;")); + llvm::cantFail(I.ParseAndExecute("x", &V1)); + EXPECT_FALSE(V1.isValid()); + EXPECT_FALSE(V1.hasValue()); } class CustomJBInterpreter : public Interpreter { @@ -139,7 +121,7 @@ TEST_F(InterpreterExtensionsTest, DefaultCrossJIT) { if (!IsARMTargetRegistered()) GTEST_SKIP(); - IncrementalCompilerBuilder CB; + clang::IncrementalCompilerBuilder CB; CB.SetTargetTriple("armv6-none-eabi"); auto CI = cantFail(CB.CreateCpp()); llvm::Error ErrOut = llvm::Error::success(); @@ -153,7 +135,7 @@ TEST_F(InterpreterExtensionsTest, CustomCrossJIT) { std::string TargetTriple = "armv6-none-eabi"; - IncrementalCompilerBuilder CB; + clang::IncrementalCompilerBuilder CB; CB.SetTargetTriple(TargetTriple); auto CI = cantFail(CB.CreateCpp());