From 0f6d68c2c1b2930e74438da67a29307c2e77b383 Mon Sep 17 00:00:00 2001 From: Vassil Vassilev Date: Sun, 16 Jul 2023 21:18:26 +0000 Subject: [PATCH 1/3] [clang-repl] Implement value printing of custom types. The work started as part of https://reviews.llvm.org/D146809 This patch reworks a lot of the infrastructure to enable printing of types for out-of-process execution on embedded devices. --- clang/include/clang/AST/ASTContext.h | 2 + .../clang/Frontend/MultiplexConsumer.h | 3 +- clang/include/clang/Interpreter/Interpreter.h | 83 +- clang/include/clang/Interpreter/Value.h | 11 +- clang/lib/Frontend/MultiplexConsumer.cpp | 7 + clang/lib/Headers/CMakeLists.txt | 1 + .../__clang_interpreter_runtime_printvalue.h | 269 ++++++ clang/lib/Interpreter/CMakeLists.txt | 1 + clang/lib/Interpreter/DeviceOffload.cpp | 10 +- clang/lib/Interpreter/DeviceOffload.h | 14 +- clang/lib/Interpreter/IncrementalExecutor.cpp | 2 +- clang/lib/Interpreter/IncrementalExecutor.h | 4 +- clang/lib/Interpreter/IncrementalParser.cpp | 253 +---- clang/lib/Interpreter/IncrementalParser.h | 45 +- clang/lib/Interpreter/Interpreter.cpp | 646 +++++-------- clang/lib/Interpreter/InterpreterUtils.cpp | 395 +++++++- clang/lib/Interpreter/InterpreterUtils.h | 18 + .../Interpreter/InterpreterValuePrinter.cpp | 890 ++++++++++++++++++ clang/lib/Interpreter/Value.cpp | 38 +- clang/lib/Parse/ParseStmt.cpp | 3 +- clang/test/Interpreter/pretty-print.c | 68 +- clang/test/Interpreter/pretty-print.cpp | 170 ++++ clang/tools/clang-repl/CMakeLists.txt | 16 + clang/tools/clang-repl/ClangRepl.cpp | 1 - .../Interpreter/CodeCompletionTest.cpp | 2 +- .../Interpreter/InterpreterExtensionsTest.cpp | 68 +- 26 files changed, 2197 insertions(+), 823 deletions(-) create mode 100644 clang/lib/Headers/__clang_interpreter_runtime_printvalue.h create mode 100644 clang/lib/Interpreter/InterpreterValuePrinter.cpp create mode 100644 clang/test/Interpreter/pretty-print.cpp 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..195545a5b393b 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,6 +36,9 @@ class ThreadSafeContext; namespace clang { class CompilerInstance; +class CodeGenerator; +class CXXRecordDecl; +class Decl; class IncrementalExecutor; class IncrementalParser; @@ -77,26 +78,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 +108,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 +137,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 +153,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 +174,29 @@ 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::unique_ptr JITBuilder; + llvm::Expected AttachValuePrinting(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..5417c550e7f98 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) \ @@ -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..0a1367f970d0f --- /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.cpp b/clang/lib/Interpreter/IncrementalExecutor.cpp index 1824a5b4570a9..4d2adecaafce7 100644 --- a/clang/lib/Interpreter/IncrementalExecutor.cpp +++ b/clang/lib/Interpreter/IncrementalExecutor.cpp @@ -118,4 +118,4 @@ IncrementalExecutor::getSymbolAddress(llvm::StringRef Name, return SymOrErr->getAddress(); } -} // end namespace clang +} // namespace clang 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..039ee4ecc99dd 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.AttachValuePrinting(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..4b92cf0c4bf97 100644 --- a/clang/lib/Interpreter/InterpreterUtils.h +++ b/clang/lib/Interpreter/InterpreterUtils.h @@ -47,7 +47,25 @@ NamespaceDecl *LookupNamespace(Sema &S, llvm::StringRef Name, NamedDecl *LookupNamed(Sema &S, llvm::StringRef Name, const DeclContext *Within); +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..a622d681a96f8 --- /dev/null +++ b/clang/lib/Interpreter/InterpreterValuePrinter.cpp @@ -0,0 +1,890 @@ +//===--- 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/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 + +using namespace clang; + +static std::string PrintDeclType(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 PrintQualType(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 PrintDeclType(NonRefTy, TTy->getDecl()); + + if (const auto *TRy = dyn_cast(NonRefTy)) + return PrintDeclType(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 PrintDeclType(NonRefTy, TDTy->getDecl()); + } + return GetFullTypeName(Ctx, NonRefTy); +} + +static std::string PrintEnum(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 << " : " << PrintQualType(Ctx, ED->getIntegerType()) << " " << APStr; + return Str; +} + +static std::string PrintFunction(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 PrintAddress(const void *Ptr, char Prefix) { + std::string Str; + llvm::raw_string_ostream SS(Str); + if (!Ptr) + return Str; + SS << Prefix << Ptr; + return Str; +} + +// FIXME: Encodings. Handle unprintable characters such as control characters. +static std::string PrintOneChar(char Val) { + std::string Str; + llvm::raw_string_ostream SS(Str); + + SS << "'" << Val << "'"; + return Str; +} + +// Char pointers +// Assumption is this is a string. +// N is limit to prevent endless loop if Ptr is not really a string. +static std::string PrintString(const char *const *Ptr, size_t N = 10000) { + std::string Str; + llvm::raw_string_ostream SS(Str); + + const char *Start = *Ptr; + if (!Start) + return "nullptr"; + + const char *End = Start + N; + // If we're gonna do this, better make sure the end is valid too + // FIXME: getpagesize() & GetSystemInfo().dwPageSize might be better + static constexpr auto PAGE_SIZE = 1024; + while (N > 1024) { + N -= PAGE_SIZE; + End = Start + N; + } + + if (*Start == 0) + return "\"\""; + + // Copy the bytes until we get a null-terminator + SS << "\""; + while (Start < End && *Start) + SS << *Start++; + SS << "\""; + + return Str; +} + +// Build the CallExpr to `PrintValueRuntime`. +static void BuildWrapperBody(Interpreter &Interp, Sema &S, ASTContext &Ctx, + FunctionDecl *WrapperFD, QualType QT, + const void *ValPtr) { + Sema::SynthesizedFunctionScope SemaFScope(S, WrapperFD); + clang::DeclarationName RuntimeCallName; + if (Ctx.getLangOpts().CPlusPlus) + RuntimeCallName = S.PP.getIdentifierInfo("PrintValueRuntime"); + else + RuntimeCallName = + S.PP.getIdentifierInfo("caas__runtime__PrintValueRuntime"); + + clang::LookupResult R(S, RuntimeCallName, SourceLocation(), + clang::Sema::LookupOrdinaryName); + S.LookupName(R, S.getCurScope()); + + 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()); + assert(!RuntimeCall.isInvalid() && "Cannot create call to PrintValueRuntime"); + + // Create the ReturnStmt. + StmtResult RetStmt = + S.ActOnReturnStmt(SourceLocation(), RuntimeCall.get(), S.getCurScope()); + assert(!RetStmt.isInvalid() && "Cannot create ReturnStmt"); + + // Create the CompoundStmt. + StmtResult Body = + CompoundStmt::Create(Ctx, {RetStmt.get()}, FPOptionsOverride(), + SourceLocation(), SourceLocation()); + assert(!Body.isInvalid() && "Cannot create function body"); + + WrapperFD->setBody(Body.get()); + // Add attribute `__attribute__((used))`. + WrapperFD->addAttr(UsedAttr::CreateImplicit(Ctx)); +} + +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; +} + +REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const void *Ptr) { + return PrintAddress(Ptr, '@'); +} + +REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const void **Ptr) { + return PrintAddress(*Ptr, '@'); +} + +REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const bool *Val) { + if (*Val) + return "true"; + return "false"; +} + +REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const char *Val) { + return PrintOneChar(*Val); +} + +REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const signed char *Val) { + return PrintOneChar(*Val); +} + +REPL_EXTERNAL_VISIBILITY std::string +PrintValueRuntime(const unsigned char *Val) { + return PrintOneChar(*Val); +} + +REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const short *Val) { + std::string Str; + llvm::raw_string_ostream SS(Str); + SS << *Val; + return Str; +} + +REPL_EXTERNAL_VISIBILITY +std::string PrintValueRuntime(const unsigned short *Val) { + std::string Str; + llvm::raw_string_ostream SS(Str); + SS << *Val; + return Str; +} + +REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const int *Val) { + std::string Str; + llvm::raw_string_ostream SS(Str); + SS << *Val; + return Str; +} + +REPL_EXTERNAL_VISIBILITY +std::string PrintValueRuntime(const unsigned int *Val) { + std::string Str; + llvm::raw_string_ostream SS(Str); + SS << *Val; + return Str; +} + +REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const long *Val) { + std::string Str; + llvm::raw_string_ostream SS(Str); + SS << *Val; + return Str; +} + +REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const long long *Val) { + std::string Str; + llvm::raw_string_ostream SS(Str); + SS << *Val; + return Str; +} + +REPL_EXTERNAL_VISIBILITY +std::string PrintValueRuntime(const unsigned long *Val) { + std::string Str; + llvm::raw_string_ostream SS(Str); + SS << *Val; + return Str; +} + +REPL_EXTERNAL_VISIBILITY +std::string PrintValueRuntime(const unsigned long long *Val) { + std::string Str; + llvm::raw_string_ostream SS(Str); + SS << *Val; + return Str; +} + +REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const float *Val) { + std::string Str; + llvm::raw_string_ostream SS(Str); + SS << llvm::format("%#.6g", *Val) << 'f'; + return Str; +} + +REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const double *Val) { + std::string Str; + llvm::raw_string_ostream SS(Str); + SS << llvm::format("%#.12g", *Val); + return Str; +} + +REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const long double *Val) { + std::string Str; + llvm::raw_string_ostream SS(Str); + SS << llvm::format("%#.8Lg", *Val) << 'L'; + return Str; +} + +REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const char *const *Val) { + return PrintString(Val); +} + +REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const char **Val) { + return PrintString(Val); +} + +namespace clang { +std::string Interpreter::ValueDataToString(const Value &V) { + QualType QT = V.getType(); + QualType DesugaredTy = QT.getDesugaredType(V.getASTContext()); + QualType NonRefTy = DesugaredTy.getNonReferenceType(); + + if (NonRefTy->isEnumeralType()) + return PrintEnum(V); + + if (NonRefTy->isFunctionType()) + return PrintFunction(V, &V); + + if ((NonRefTy->isPointerType() || NonRefTy->isMemberPointerType()) && + NonRefTy->getPointeeType()->isFunctionProtoType()) + return PrintFunction(V, V.getPtr()); + + if (NonRefTy->isNullPtrType()) + return "nullptr\n"; + + // If it is a builtin type dispatch to the builtin overloads. + if (auto *BT = DesugaredTy.getCanonicalType()->getAs()) { + switch (BT->getKind()) { + default: + return "{ unknown builtin type: }" + std::to_string(BT->getKind()); +#define X(type, name) \ + case clang::BuiltinType::name: { \ + type val = V.get##name(); \ + return PrintValueRuntime(&val); \ + } + REPL_BUILTIN_TYPES +#undef X + } + } + if (auto *CXXRD = NonRefTy->getAsCXXRecordDecl()) + if (CXXRD->isLambda()) + return PrintAddress(V.getPtr(), '@'); + + // All fails then generate a runtime call, this is slow. + Sema &S = getCompilerInstance()->getSema(); + ASTContext &Ctx = S.getASTContext(); + + QualType RetTy; + if (Ctx.getLangOpts().CPlusPlus && !StdString) { + + // Only include the header on demand because it's very heavy. + if (llvm::Error E = ParseAndExecute( + "#include <__clang_interpreter_runtime_printvalue.h>")) { + llvm::logAllUnhandledErrors(std::move(E), llvm::errs(), "Parsing failed"); + return "{Internal error}"; + } + + // Find and cache std::string. + NamespaceDecl *Std = LookupNamespace(S, "std"); + assert(Std && "Cannot find namespace std"); + Decl *StdStringDecl = LookupNamed(S, "string", Std); + assert(StdStringDecl && "Cannot find std::string"); + const auto *StdStringTyDecl = llvm::dyn_cast(StdStringDecl); + assert(StdStringTyDecl && "Cannot find type of std::string"); + RetTy = QualType(StdStringTyDecl->getTypeForDecl(), /*Quals=*/0); + } else { + RetTy = Ctx.getPointerType(Ctx.CharTy.withConst()); + } + + // 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(); + + BuildWrapperBody(*this, S, Ctx, WrapperFD, V.getType(), ValPtr); + + auto AddrOrErr = CompileDecl(*this, WrapperFD); + if (!AddrOrErr) + llvm::logAllUnhandledErrors(AddrOrErr.takeError(), llvm::errs(), + "Fail to get symbol address"); + if (auto *Main = AddrOrErr->toPtr()) + return (*Main)(); + return "Unable to print the value!"; +} + +std::string Interpreter::ValueTypeToString(const Value &V) const { + ASTContext &Ctx = const_cast(V.getASTContext()); + QualType QT = V.getType(); + + std::string QTStr = PrintQualType(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) { + return InterfaceKind::WithAlloc; + } + + 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()); + } +}; + +// 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); +llvm::Expected Interpreter::AttachValuePrinting(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(); + }; + static constexpr llvm::StringRef Builtin[] = { + "__clang_Interpreter_SetValueNoAlloc", + "__clang_Interpreter_SetValueWithAlloc", + "__clang_Interpreter_SetValueCopyArr", "__ci_newtag"}; + if (llvm::Error Err = + LookupInterface(ValuePrintingInfo[NoAlloc], Builtin[NoAlloc])) + return std::move(Err); + + if (Ctx.getLangOpts().CPlusPlus) { + if (llvm::Error Err = + LookupInterface(ValuePrintingInfo[WithAlloc], Builtin[WithAlloc])) + return std::move(Err); + if (llvm::Error Err = + LookupInterface(ValuePrintingInfo[CopyArray], Builtin[CopyArray])) + return std::move(Err); + if (llvm::Error Err = + LookupInterface(ValuePrintingInfo[NewTag], Builtin[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); + 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()); + assert(!AllocCall.isInvalid() && "Can't create runtime interface call!"); + + 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()); + } + 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); + + assert(!CXXNewCall.isInvalid() && + "Can't create runtime placement new call!"); + + 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..927cd836ab693 100644 --- a/clang/test/Interpreter/pretty-print.c +++ b/clang/test/Interpreter/pretty-print.c @@ -3,9 +3,73 @@ // 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]+}} + +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..b7a6c07c481dd --- /dev/null +++ b/clang/test/Interpreter/pretty-print.cpp @@ -0,0 +1,170 @@ +// 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* PrintValueRuntime(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 } } + +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 PrintValueRuntime(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: (MyType) My pretty printer! +%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..f4efb5e1b49ee 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))); 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()); From 24d0867459582939915b4a8175bcc2f2f2aede33 Mon Sep 17 00:00:00 2001 From: Vassil Vassilev Date: Sat, 31 Aug 2024 13:21:26 +0000 Subject: [PATCH 2/3] store --- clang/include/clang/Interpreter/Interpreter.h | 2 +- .../__clang_interpreter_runtime_printvalue.h | 28 +-- clang/lib/Interpreter/IncrementalExecutor.cpp | 2 +- clang/lib/Interpreter/Interpreter.cpp | 2 +- .../Interpreter/InterpreterValuePrinter.cpp | 205 ++++++++++++------ clang/test/Interpreter/pretty-print.c | 5 +- clang/test/Interpreter/pretty-print.cpp | 2 +- 7 files changed, 158 insertions(+), 88 deletions(-) diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h index 195545a5b393b..f394c8bf21e63 100644 --- a/clang/include/clang/Interpreter/Interpreter.h +++ b/clang/include/clang/Interpreter/Interpreter.h @@ -187,7 +187,7 @@ class Interpreter { std::string ValueDataToString(const Value &V); std::string ValueTypeToString(const Value &V) const; - llvm::Expected AttachValuePrinting(Expr *E); + 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. diff --git a/clang/lib/Headers/__clang_interpreter_runtime_printvalue.h b/clang/lib/Headers/__clang_interpreter_runtime_printvalue.h index 0a1367f970d0f..38c20a5390387 100644 --- a/clang/lib/Headers/__clang_interpreter_runtime_printvalue.h +++ b/clang/lib/Headers/__clang_interpreter_runtime_printvalue.h @@ -60,20 +60,20 @@ __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); +// __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 { diff --git a/clang/lib/Interpreter/IncrementalExecutor.cpp b/clang/lib/Interpreter/IncrementalExecutor.cpp index 4d2adecaafce7..1824a5b4570a9 100644 --- a/clang/lib/Interpreter/IncrementalExecutor.cpp +++ b/clang/lib/Interpreter/IncrementalExecutor.cpp @@ -118,4 +118,4 @@ IncrementalExecutor::getSymbolAddress(llvm::StringRef Name, return SymOrErr->getAddress(); } -} // namespace clang +} // end namespace clang diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp index 039ee4ecc99dd..b4cf63f00e0be 100644 --- a/clang/lib/Interpreter/Interpreter.cpp +++ b/clang/lib/Interpreter/Interpreter.cpp @@ -262,7 +262,7 @@ class InProcessPrintingASTConsumer final : public MultiplexConsumer { if (auto *TLSD = llvm::dyn_cast(D)) if (TLSD && TLSD->isSemiMissing()) { auto ExprOrErr = - Interp.AttachValuePrinting(cast(TLSD->getStmt())); + Interp.convertExprToValue(cast(TLSD->getStmt())); if (llvm::Error E = ExprOrErr.takeError()) { llvm::logAllUnhandledErrors(std::move(E), llvm::errs(), "Value printing failed: "); diff --git a/clang/lib/Interpreter/InterpreterValuePrinter.cpp b/clang/lib/Interpreter/InterpreterValuePrinter.cpp index a622d681a96f8..e9269c4452da9 100644 --- a/clang/lib/Interpreter/InterpreterValuePrinter.cpp +++ b/clang/lib/Interpreter/InterpreterValuePrinter.cpp @@ -14,6 +14,7 @@ #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" @@ -26,8 +27,11 @@ #include "llvm/Support/raw_ostream.h" #include +#include #include +#define DEBUG_TYPE "interp-value" + using namespace clang; static std::string PrintDeclType(const QualType &QT, NamedDecl *D) { @@ -202,20 +206,11 @@ static std::string PrintString(const char *const *Ptr, size_t N = 10000) { } // Build the CallExpr to `PrintValueRuntime`. -static void BuildWrapperBody(Interpreter &Interp, Sema &S, ASTContext &Ctx, - FunctionDecl *WrapperFD, QualType QT, - const void *ValPtr) { +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; - if (Ctx.getLangOpts().CPlusPlus) - RuntimeCallName = S.PP.getIdentifierInfo("PrintValueRuntime"); - else - RuntimeCallName = - S.PP.getIdentifierInfo("caas__runtime__PrintValueRuntime"); - - clang::LookupResult R(S, RuntimeCallName, SourceLocation(), - clang::Sema::LookupOrdinaryName); - S.LookupName(R, S.getCurScope()); Expr *OverldExpr = UnresolvedLookupExpr::Create( Ctx, /*NamingClass=*/nullptr, NestedNameSpecifierLoc(), @@ -254,22 +249,33 @@ static void BuildWrapperBody(Interpreter &Interp, Sema &S, ASTContext &Ctx, ExprResult RuntimeCall = S.ActOnCallExpr(S.getCurScope(), OverldExpr, SourceLocation(), CallArgs, SourceLocation()); - assert(!RuntimeCall.isInvalid() && "Cannot create call to PrintValueRuntime"); + 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()); - assert(!RetStmt.isInvalid() && "Cannot create ReturnStmt"); + + 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()); - assert(!Body.isInvalid() && "Cannot create function body"); + 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"; @@ -423,21 +429,42 @@ REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const char **Val) { return PrintString(Val); } +// Check if the user has implemented a function that avoids fallback to +// defaults. +static clang::LookupResult LookupUserDefined(Sema &S, QualType QT) { + PrintingPolicy Policy = S.getASTContext().getPrintingPolicy(); + Policy.SuppressElaboration = 1; + Policy.SuppressTagKeyword = 1; + Policy.FullyQualifiedName = 1; + std::string TypeStr = QT.getAsString(Policy); + DeclarationName 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::ValueDataToString(const Value &V) { + Sema &S = getCompilerInstance()->getSema(); + ASTContext &Ctx = S.getASTContext(); + QualType QT = V.getType(); - QualType DesugaredTy = QT.getDesugaredType(V.getASTContext()); + QualType DesugaredTy = QT.getDesugaredType(Ctx); QualType NonRefTy = DesugaredTy.getNonReferenceType(); - if (NonRefTy->isEnumeralType()) - return PrintEnum(V); + if ((NonRefTy->isPointerType() || NonRefTy->isMemberPointerType()) && + NonRefTy->getPointeeType()->isFunctionProtoType()) + return PrintFunction(V, V.getPtr()); if (NonRefTy->isFunctionType()) return PrintFunction(V, &V); - if ((NonRefTy->isPointerType() || NonRefTy->isMemberPointerType()) && - NonRefTy->getPointeeType()->isFunctionProtoType()) - return PrintFunction(V, V.getPtr()); + if (NonRefTy->isEnumeralType()) + return PrintEnum(V); if (NonRefTy->isNullPtrType()) return "nullptr\n"; @@ -446,46 +473,52 @@ std::string Interpreter::ValueDataToString(const Value &V) { if (auto *BT = DesugaredTy.getCanonicalType()->getAs()) { switch (BT->getKind()) { default: - return "{ unknown builtin type: }" + std::to_string(BT->getKind()); -#define X(type, name) \ - case clang::BuiltinType::name: { \ - type val = V.get##name(); \ - return PrintValueRuntime(&val); \ - } - REPL_BUILTIN_TYPES -#undef X + return "{ error: unknown builtin type '" + std::to_string(BT->getKind()) +" '}"; + case clang::BuiltinType::Bool: { bool val = V.getBool(); return PrintValueRuntime(&val); } case clang::BuiltinType::Char_S: { char val = V.getChar_S(); return PrintValueRuntime(&val); } case clang::BuiltinType::SChar: { signed char val = V.getSChar(); return PrintValueRuntime(&val); } case clang::BuiltinType::Char_U: { unsigned char val = V.getChar_U(); return PrintValueRuntime(&val); } case clang::BuiltinType::UChar: { unsigned char val = V.getUChar(); return PrintValueRuntime(&val); } case clang::BuiltinType::Short: { short val = V.getShort(); return PrintValueRuntime(&val); } case clang::BuiltinType::UShort: { unsigned short val = V.getUShort(); return PrintValueRuntime(&val); } case clang::BuiltinType::Int: { int val = V.getInt(); return PrintValueRuntime(&val); } case clang::BuiltinType::UInt: { unsigned int val = V.getUInt(); return PrintValueRuntime(&val); } case clang::BuiltinType::Long: { long val = V.getLong(); return PrintValueRuntime(&val); } case clang::BuiltinType::ULong: { unsigned long val = V.getULong(); return PrintValueRuntime(&val); } case clang::BuiltinType::LongLong: { long long val = V.getLongLong(); return PrintValueRuntime(&val); } case clang::BuiltinType::ULongLong: { unsigned long long val = V.getULongLong(); return PrintValueRuntime(&val); } case clang::BuiltinType::Float: { float val = V.getFloat(); return PrintValueRuntime(&val); } case clang::BuiltinType::Double: { double val = V.getDouble(); return PrintValueRuntime(&val); } case clang::BuiltinType::LongDouble: { long double val = V.getLongDouble(); return PrintValueRuntime(&val); } } } if (auto *CXXRD = NonRefTy->getAsCXXRecordDecl()) if (CXXRD->isLambda()) return PrintAddress(V.getPtr(), '@'); - // All fails then generate a runtime call, this is slow. - Sema &S = getCompilerInstance()->getSema(); - ASTContext &Ctx = S.getASTContext(); + // FIXME: Add support for custom printers in C. + if (NonRefTy->isPointerType()) { + if (NonRefTy->getPointeeType()->isCharType()) + return PrintValueRuntime((const char**)V.getPtrAddress()); + return PrintValueRuntime(V.getPtr()); + } - QualType RetTy; - if (Ctx.getLangOpts().CPlusPlus && !StdString) { + QualType RetTy = Ctx.getPointerType(Ctx.CharTy.withConst()); - // Only include the header on demand because it's very heavy. - if (llvm::Error E = ParseAndExecute( - "#include <__clang_interpreter_runtime_printvalue.h>")) { - llvm::logAllUnhandledErrors(std::move(E), llvm::errs(), "Parsing failed"); - return "{Internal error}"; - } + LookupResult R = LookupUserDefined(S, QT); + if (!R.isSingleResult()) + return "{error: multiple results for '" + R.getLookupName().getAsString() + "'}"; - // Find and cache std::string. - NamespaceDecl *Std = LookupNamespace(S, "std"); - assert(Std && "Cannot find namespace std"); - Decl *StdStringDecl = LookupNamed(S, "string", Std); - assert(StdStringDecl && "Cannot find std::string"); - const auto *StdStringTyDecl = llvm::dyn_cast(StdStringDecl); - assert(StdStringTyDecl && "Cannot find type of std::string"); - RetTy = QualType(StdStringTyDecl->getTypeForDecl(), /*Quals=*/0); - } else { - RetTy = Ctx.getPointerType(Ctx.CharTy.withConst()); + if (R.empty()) { + + if (!Ctx.getLangOpts().CPlusPlus) + return PrintValueRuntime(V.getPtr()); + + if (S.StdNamespace && !StdString) { + + // Only include the header on demand because it's very heavy. + auto TUorE = Parse("#include <__clang_interpreter_runtime_printvalue.h>"); + if (llvm::Error E = TUorE.takeError() ) { + llvm::logAllUnhandledErrors(std::move(E), llvm::errs(), "Parsing failed"); + return "{error: cannot parse system header}"; + } + + // Find and cache std::string. + Decl *StdStringDecl = LookupNamed(S, "string", S.getStdNamespace()); + assert(StdStringDecl && "Cannot find std::string"); + const auto *StdStringTyDecl = llvm::dyn_cast(StdStringDecl); + assert(StdStringTyDecl && "Cannot find type of std::string"); + RetTy = QualType(StdStringTyDecl->getTypeForDecl(), /*Quals=*/0); + } } + // All fails then generate a runtime call, this is slow. + // Create the wrapper function. DeclarationName DeclName = &Ctx.Idents.get(CreateUniqName(WrapperName)); QualType FnTy = @@ -501,7 +534,30 @@ std::string Interpreter::ValueDataToString(const Value &V) { if (!V.isManuallyAlloc()) ValPtr = V.getPtrAddress(); - BuildWrapperBody(*this, S, Ctx, WrapperFD, V.getType(), ValPtr); + // // Check if the user has implemented a function that avoids fallback to + // // defaults. + // if (Ctx.getLangOpts().CPlusPlus) { + // RuntimeCallName = S.PP.getIdentifierInfo("PrintValueRuntime"); + // } else { + // PrintingPolicy Policy = Ctx.getPrintingPolicy(); + // Policy.SuppressElaboration = 1; + // Policy.SuppressTagKeyword = 1; + // Policy.FullyQualifiedName = 1; + // std::string TypeStr = QT.getAsString(Policy); + // printf("KKK %s\n", TypeStr.c_str()); + // // clang::TypeName::getFullyQualifiedName(QT, Ctx, Policy); + // RuntimeCallName = + // S.PP.getIdentifierInfo("caas__runtime__PrintValueRuntime__" + TypeStr); + // } + + // LookupResult R(S, RuntimeCallName, SourceLocation(),Sema::LookupOrdinaryName); + // S.LookupName(R, S.getCurScope()); + + 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 "{Unable to print the value!}"; + } auto AddrOrErr = CompileDecl(*this, WrapperFD); if (!AddrOrErr) @@ -509,7 +565,7 @@ std::string Interpreter::ValueDataToString(const Value &V) { "Fail to get symbol address"); if (auto *Main = AddrOrErr->toPtr()) return (*Main)(); - return "Unable to print the value!"; + return "{Unable to print the value!}"; } std::string Interpreter::ValueTypeToString(const Value &V) const { @@ -564,7 +620,12 @@ class InterfaceKindVisitor } InterfaceKind VisitRecordType(const RecordType *Ty) { - return InterfaceKind::WithAlloc; + 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) { @@ -635,9 +696,14 @@ class InterfaceKindVisitor } }; +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: +// In general, we transform c++: // clang-repl> x // To: // // 1. If x is a built-in type like int, float. @@ -648,7 +714,7 @@ class InterfaceKindVisitor // // 3. If x is a struct, but a rvalue. // new (__clang_Interpreter_SetValueWithAlloc(ThisInterp, OpaqueValue, // xQualType)) (x); -llvm::Expected Interpreter::AttachValuePrinting(Expr *E) { +llvm::Expected Interpreter::convertExprToValue(Expr *E) { Sema &S = getCompilerInstance()->getSema(); ASTContext &Ctx = S.getASTContext(); @@ -670,23 +736,19 @@ llvm::Expected Interpreter::AttachValuePrinting(Expr *E) { Interface = S.BuildDeclarationNameExpr(CSS, R, /*ADL=*/false).get(); return llvm::Error::success(); }; - static constexpr llvm::StringRef Builtin[] = { - "__clang_Interpreter_SetValueNoAlloc", - "__clang_Interpreter_SetValueWithAlloc", - "__clang_Interpreter_SetValueCopyArr", "__ci_newtag"}; if (llvm::Error Err = - LookupInterface(ValuePrintingInfo[NoAlloc], Builtin[NoAlloc])) + LookupInterface(ValuePrintingInfo[NoAlloc], VPName[NoAlloc])) return std::move(Err); if (Ctx.getLangOpts().CPlusPlus) { if (llvm::Error Err = - LookupInterface(ValuePrintingInfo[WithAlloc], Builtin[WithAlloc])) + LookupInterface(ValuePrintingInfo[WithAlloc], VPName[WithAlloc])) return std::move(Err); if (llvm::Error Err = - LookupInterface(ValuePrintingInfo[CopyArray], Builtin[CopyArray])) + LookupInterface(ValuePrintingInfo[CopyArray], VPName[CopyArray])) return std::move(Err); if (llvm::Error Err = - LookupInterface(ValuePrintingInfo[NewTag], Builtin[NewTag])) + LookupInterface(ValuePrintingInfo[NewTag], VPName[NewTag])) return std::move(Err); } } @@ -725,6 +787,8 @@ llvm::Expected Interpreter::AttachValuePrinting(Expr *E) { 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; @@ -733,7 +797,9 @@ llvm::Expected Interpreter::AttachValuePrinting(Expr *E) { ExprResult AllocCall = S.ActOnCallExpr(Scope, ValuePrintingInfo[InterfaceKind::WithAlloc], E->getBeginLoc(), AdjustedArgs, E->getEndLoc()); - assert(!AllocCall.isInvalid() && "Can't create runtime interface call!"); + if (AllocCall.isInvalid()) + return llvm::make_error("Cannot call to " + VPName[WithAlloc], + llvm::inconvertibleErrorCode()); TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ty, SourceLocation()); @@ -755,6 +821,11 @@ llvm::Expected Interpreter::AttachValuePrinting(Expr *E) { 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( @@ -764,8 +835,10 @@ llvm::Expected Interpreter::AttachValuePrinting(Expr *E) { /*TypeIdParens=*/SourceRange(), TSI->getType(), TSI, std::nullopt, E->getSourceRange(), E); - assert(!CXXNewCall.isInvalid() && - "Can't create runtime placement new call!"); + if (CXXNewCall.isInvalid()) + return llvm::make_error( + "Cannot build a call to placement new", + llvm::inconvertibleErrorCode()); SetValueE = S.ActOnFinishFullExpr(CXXNewCall.get(), /*DiscardedValue=*/false); diff --git a/clang/test/Interpreter/pretty-print.c b/clang/test/Interpreter/pretty-print.c index 927cd836ab693..63e63631640a3 100644 --- a/clang/test/Interpreter/pretty-print.c +++ b/clang/test/Interpreter/pretty-print.c @@ -37,9 +37,6 @@ double d = 4.21; d struct S1{} s1; s1 // CHECK-NEXT: (S1 &) @0x{{[0-9a-f]+}} -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 @@ -62,7 +59,7 @@ const char * caas__runtime__PrintValueRuntime(const struct ConstLiteral *) { \ struct ConstLiteral CL; CL // CHECK-NEXT: ConstLiteral -struct Point{int x; int y}; +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); \ diff --git a/clang/test/Interpreter/pretty-print.cpp b/clang/test/Interpreter/pretty-print.cpp index b7a6c07c481dd..87f595fd03360 100644 --- a/clang/test/Interpreter/pretty-print.cpp +++ b/clang/test/Interpreter/pretty-print.cpp @@ -165,6 +165,6 @@ namespace caas { namespace runtime { \ } \ }} MyDate{2024, 8, 23} -// CHECK-NEXT: (MyType) My pretty printer! +// CHECK-NEXT: (MyDate) 2024-Aug-23 %quit From 9554375738fcfaebab36e41f57618dafc5d7264c Mon Sep 17 00:00:00 2001 From: Vassil Vassilev Date: Fri, 6 Sep 2024 09:00:11 +0000 Subject: [PATCH 3/3] store --- clang/include/clang/Interpreter/Interpreter.h | 2 + clang/include/clang/Interpreter/Value.h | 2 +- clang/lib/Interpreter/InterpreterUtils.h | 2 +- .../Interpreter/InterpreterValuePrinter.cpp | 511 ++++++++---------- clang/test/Interpreter/pretty-print.cpp | 8 +- clang/tools/clang-repl/ClangRepl.cpp | 2 +- 6 files changed, 239 insertions(+), 288 deletions(-) diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h index f394c8bf21e63..1763afd240a2c 100644 --- a/clang/include/clang/Interpreter/Interpreter.h +++ b/clang/include/clang/Interpreter/Interpreter.h @@ -41,6 +41,7 @@ class CXXRecordDecl; class Decl; class IncrementalExecutor; class IncrementalParser; +class LookupResult; /// Create a pre-configured \c CompilerInstance for incremental processing. class IncrementalCompilerBuilder { @@ -186,6 +187,7 @@ class Interpreter { std::string ValueDataToString(const Value &V); std::string ValueTypeToString(const Value &V) const; + std::string CallUserSpecifiedPrinter(LookupResult &R, const Value &V); llvm::Expected convertExprToValue(Expr *E); diff --git a/clang/include/clang/Interpreter/Value.h b/clang/include/clang/Interpreter/Value.h index 5417c550e7f98..664f9e9008603 100644 --- a/clang/include/clang/Interpreter/Value.h +++ b/clang/include/clang/Interpreter/Value.h @@ -178,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(); } diff --git a/clang/lib/Interpreter/InterpreterUtils.h b/clang/lib/Interpreter/InterpreterUtils.h index 4b92cf0c4bf97..bd23f249551b5 100644 --- a/clang/lib/Interpreter/InterpreterUtils.h +++ b/clang/lib/Interpreter/InterpreterUtils.h @@ -45,7 +45,7 @@ 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); diff --git a/clang/lib/Interpreter/InterpreterValuePrinter.cpp b/clang/lib/Interpreter/InterpreterValuePrinter.cpp index e9269c4452da9..df2ee3edcc156 100644 --- a/clang/lib/Interpreter/InterpreterValuePrinter.cpp +++ b/clang/lib/Interpreter/InterpreterValuePrinter.cpp @@ -28,13 +28,14 @@ #include #include +#include #include #define DEBUG_TYPE "interp-value" using namespace clang; -static std::string PrintDeclType(const QualType &QT, NamedDecl *D) { +static std::string DeclTypeToString(const QualType &QT, NamedDecl *D) { std::string Str; llvm::raw_string_ostream SS(Str); if (QT.hasQualifiers()) @@ -43,7 +44,7 @@ static std::string PrintDeclType(const QualType &QT, NamedDecl *D) { return Str; } -static std::string PrintQualType(ASTContext &Ctx, QualType QT) { +static std::string QualTypeToString(ASTContext &Ctx, QualType QT) { PrintingPolicy Policy(Ctx.getPrintingPolicy()); // Print the Allocator in STL containers, for instance. Policy.SuppressDefaultTemplateArgs = false; @@ -65,10 +66,10 @@ static std::string PrintQualType(ASTContext &Ctx, QualType QT) { const QualType NonRefTy = QT.getNonReferenceType(); if (const auto *TTy = llvm::dyn_cast(NonRefTy)) - return PrintDeclType(NonRefTy, TTy->getDecl()); + return DeclTypeToString(NonRefTy, TTy->getDecl()); if (const auto *TRy = dyn_cast(NonRefTy)) - return PrintDeclType(NonRefTy, TRy->getDecl()); + return DeclTypeToString(NonRefTy, TRy->getDecl()); const QualType Canon = NonRefTy.getCanonicalType(); @@ -90,12 +91,12 @@ static std::string PrintQualType(ASTContext &Ctx, QualType QT) { return GetFullTypeName(Ctx, Canon); else if (llvm::isa(SSDesugar)) return GetFullTypeName(Ctx, NonRefTy); - return PrintDeclType(NonRefTy, TDTy->getDecl()); + return DeclTypeToString(NonRefTy, TDTy->getDecl()); } return GetFullTypeName(Ctx, NonRefTy); } -static std::string PrintEnum(const Value &V) { +static std::string EnumToString(const Value &V) { std::string Str; llvm::raw_string_ostream SS(Str); ASTContext &Ctx = const_cast(V.getASTContext()); @@ -119,11 +120,11 @@ static std::string PrintEnum(const Value &V) { } llvm::SmallString<64> APStr; AP.toString(APStr, /*Radix=*/10); - SS << " : " << PrintQualType(Ctx, ED->getIntegerType()) << " " << APStr; + SS << " : " << QualTypeToString(Ctx, ED->getIntegerType()) << " " << APStr; return Str; } -static std::string PrintFunction(const Value &V, const void *Ptr) { +static std::string FunctionToString(const Value &V, const void *Ptr) { std::string Str; llvm::raw_string_ostream SS(Str); SS << "Function @" << Ptr; @@ -155,7 +156,7 @@ static std::string PrintFunction(const Value &V, const void *Ptr) { return Str; } -static std::string PrintAddress(const void *Ptr, char Prefix) { +static std::string AddressToString(const void *Ptr, char Prefix) { std::string Str; llvm::raw_string_ostream SS(Str); if (!Ptr) @@ -164,51 +165,10 @@ static std::string PrintAddress(const void *Ptr, char Prefix) { return Str; } -// FIXME: Encodings. Handle unprintable characters such as control characters. -static std::string PrintOneChar(char Val) { - std::string Str; - llvm::raw_string_ostream SS(Str); - - SS << "'" << Val << "'"; - return Str; -} - -// Char pointers -// Assumption is this is a string. -// N is limit to prevent endless loop if Ptr is not really a string. -static std::string PrintString(const char *const *Ptr, size_t N = 10000) { - std::string Str; - llvm::raw_string_ostream SS(Str); - - const char *Start = *Ptr; - if (!Start) - return "nullptr"; - - const char *End = Start + N; - // If we're gonna do this, better make sure the end is valid too - // FIXME: getpagesize() & GetSystemInfo().dwPageSize might be better - static constexpr auto PAGE_SIZE = 1024; - while (N > 1024) { - N -= PAGE_SIZE; - End = Start + N; - } - - if (*Start == 0) - return "\"\""; - - // Copy the bytes until we get a null-terminator - SS << "\""; - while (Start < End && *Start) - SS << *Start++; - SS << "\""; - - return Str; -} - // Build the CallExpr to `PrintValueRuntime`. -static llvm::Error BuildWrapperBody(LookupResult &R, Sema &S, - ASTContext &Ctx, FunctionDecl *WrapperFD, - QualType QT, const void *ValPtr) { +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; @@ -251,8 +211,8 @@ static llvm::Error BuildWrapperBody(LookupResult &R, Sema &S, SourceLocation()); if (RuntimeCall.isInvalid()) { std::string Name = R.getLookupName().getAsString(); - return llvm::make_error( - "Cannot create call to " + Name, llvm::inconvertibleErrorCode()); + return llvm::make_error("Cannot create call to " + Name, + llvm::inconvertibleErrorCode()); } // Create the ReturnStmt. @@ -313,212 +273,81 @@ static std::string CreateUniqName(std::string Base) { return Base; } -REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const void *Ptr) { - return PrintAddress(Ptr, '@'); -} - -REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const void **Ptr) { - return PrintAddress(*Ptr, '@'); -} - -REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const bool *Val) { - if (*Val) - return "true"; - return "false"; -} - -REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const char *Val) { - return PrintOneChar(*Val); -} - -REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const signed char *Val) { - return PrintOneChar(*Val); -} - -REPL_EXTERNAL_VISIBILITY std::string -PrintValueRuntime(const unsigned char *Val) { - return PrintOneChar(*Val); -} - -REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const short *Val) { - std::string Str; - llvm::raw_string_ostream SS(Str); - SS << *Val; - return Str; -} - -REPL_EXTERNAL_VISIBILITY -std::string PrintValueRuntime(const unsigned short *Val) { - std::string Str; - llvm::raw_string_ostream SS(Str); - SS << *Val; - return Str; -} - -REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const int *Val) { - std::string Str; - llvm::raw_string_ostream SS(Str); - SS << *Val; - return Str; -} - -REPL_EXTERNAL_VISIBILITY -std::string PrintValueRuntime(const unsigned int *Val) { - std::string Str; - llvm::raw_string_ostream SS(Str); - SS << *Val; - return Str; -} - -REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const long *Val) { - std::string Str; - llvm::raw_string_ostream SS(Str); - SS << *Val; - return Str; -} - -REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const long long *Val) { - std::string Str; - llvm::raw_string_ostream SS(Str); - SS << *Val; - return Str; -} - -REPL_EXTERNAL_VISIBILITY -std::string PrintValueRuntime(const unsigned long *Val) { - std::string Str; - llvm::raw_string_ostream SS(Str); - SS << *Val; - return Str; -} - -REPL_EXTERNAL_VISIBILITY -std::string PrintValueRuntime(const unsigned long long *Val) { - std::string Str; - llvm::raw_string_ostream SS(Str); - SS << *Val; - return Str; -} - -REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const float *Val) { - std::string Str; - llvm::raw_string_ostream SS(Str); - SS << llvm::format("%#.6g", *Val) << 'f'; - return Str; -} - -REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const double *Val) { - std::string Str; - llvm::raw_string_ostream SS(Str); - SS << llvm::format("%#.12g", *Val); - return Str; -} - -REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const long double *Val) { - std::string Str; - llvm::raw_string_ostream SS(Str); - SS << llvm::format("%#.8Lg", *Val) << 'L'; - return Str; -} - -REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const char *const *Val) { - return PrintString(Val); -} - -REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const char **Val) { - return PrintString(Val); -} - // Check if the user has implemented a function that avoids fallback to // defaults. static clang::LookupResult LookupUserDefined(Sema &S, QualType QT) { - PrintingPolicy Policy = S.getASTContext().getPrintingPolicy(); - Policy.SuppressElaboration = 1; - Policy.SuppressTagKeyword = 1; - Policy.FullyQualifiedName = 1; - std::string TypeStr = QT.getAsString(Policy); - DeclarationName 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); + 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::ValueDataToString(const Value &V) { +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(); - QualType QT = V.getType(); - QualType DesugaredTy = QT.getDesugaredType(Ctx); - QualType NonRefTy = DesugaredTy.getNonReferenceType(); - - if ((NonRefTy->isPointerType() || NonRefTy->isMemberPointerType()) && - NonRefTy->getPointeeType()->isFunctionProtoType()) - return PrintFunction(V, V.getPtr()); - - if (NonRefTy->isFunctionType()) - return PrintFunction(V, &V); - - if (NonRefTy->isEnumeralType()) - return PrintEnum(V); - - if (NonRefTy->isNullPtrType()) - return "nullptr\n"; - - // If it is a builtin type dispatch to the builtin overloads. - if (auto *BT = DesugaredTy.getCanonicalType()->getAs()) { - switch (BT->getKind()) { - default: - return "{ error: unknown builtin type '" + std::to_string(BT->getKind()) +" '}"; - case clang::BuiltinType::Bool: { bool val = V.getBool(); return PrintValueRuntime(&val); } case clang::BuiltinType::Char_S: { char val = V.getChar_S(); return PrintValueRuntime(&val); } case clang::BuiltinType::SChar: { signed char val = V.getSChar(); return PrintValueRuntime(&val); } case clang::BuiltinType::Char_U: { unsigned char val = V.getChar_U(); return PrintValueRuntime(&val); } case clang::BuiltinType::UChar: { unsigned char val = V.getUChar(); return PrintValueRuntime(&val); } case clang::BuiltinType::Short: { short val = V.getShort(); return PrintValueRuntime(&val); } case clang::BuiltinType::UShort: { unsigned short val = V.getUShort(); return PrintValueRuntime(&val); } case clang::BuiltinType::Int: { int val = V.getInt(); return PrintValueRuntime(&val); } case clang::BuiltinType::UInt: { unsigned int val = V.getUInt(); return PrintValueRuntime(&val); } case clang::BuiltinType::Long: { long val = V.getLong(); return PrintValueRuntime(&val); } case clang::BuiltinType::ULong: { unsigned long val = V.getULong(); return PrintValueRuntime(&val); } case clang::BuiltinType::LongLong: { long long val = V.getLongLong(); return PrintValueRuntime(&val); } case clang::BuiltinType::ULongLong: { unsigned long long val = V.getULongLong(); return PrintValueRuntime(&val); } case clang::BuiltinType::Float: { float val = V.getFloat(); return PrintValueRuntime(&val); } case clang::BuiltinType::Double: { double val = V.getDouble(); return PrintValueRuntime(&val); } case clang::BuiltinType::LongDouble: { long double val = V.getLongDouble(); return PrintValueRuntime(&val); } - } - } - if (auto *CXXRD = NonRefTy->getAsCXXRecordDecl()) - if (CXXRD->isLambda()) - return PrintAddress(V.getPtr(), '@'); - - // FIXME: Add support for custom printers in C. - if (NonRefTy->isPointerType()) { - if (NonRefTy->getPointeeType()->isCharType()) - return PrintValueRuntime((const char**)V.getPtrAddress()); - return PrintValueRuntime(V.getPtr()); - } - + // Check the found candidates. QualType RetTy = Ctx.getPointerType(Ctx.CharTy.withConst()); - - LookupResult R = LookupUserDefined(S, QT); - if (!R.isSingleResult()) - return "{error: multiple results for '" + R.getLookupName().getAsString() + "'}"; - - if (R.empty()) { - - if (!Ctx.getLangOpts().CPlusPlus) - return PrintValueRuntime(V.getPtr()); - - if (S.StdNamespace && !StdString) { - - // Only include the header on demand because it's very heavy. - auto TUorE = Parse("#include <__clang_interpreter_runtime_printvalue.h>"); - if (llvm::Error E = TUorE.takeError() ) { - llvm::logAllUnhandledErrors(std::move(E), llvm::errs(), "Parsing failed"); - return "{error: cannot parse system header}"; - } - - // Find and cache std::string. - Decl *StdStringDecl = LookupNamed(S, "string", S.getStdNamespace()); - assert(StdStringDecl && "Cannot find std::string"); - const auto *StdStringTyDecl = llvm::dyn_cast(StdStringDecl); - assert(StdStringTyDecl && "Cannot find type of std::string"); - RetTy = QualType(StdStringTyDecl->getTypeForDecl(), /*Quals=*/0); + 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); } } - // All fails then generate a runtime call, this is slow. - // Create the wrapper function. DeclarationName DeclName = &Ctx.Idents.get(CreateUniqName(WrapperName)); QualType FnTy = @@ -531,48 +360,161 @@ std::string Interpreter::ValueDataToString(const Value &V) { // 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(); - - // // Check if the user has implemented a function that avoids fallback to - // // defaults. - // if (Ctx.getLangOpts().CPlusPlus) { - // RuntimeCallName = S.PP.getIdentifierInfo("PrintValueRuntime"); - // } else { - // PrintingPolicy Policy = Ctx.getPrintingPolicy(); - // Policy.SuppressElaboration = 1; - // Policy.SuppressTagKeyword = 1; - // Policy.FullyQualifiedName = 1; - // std::string TypeStr = QT.getAsString(Policy); - // printf("KKK %s\n", TypeStr.c_str()); - // // clang::TypeName::getFullyQualifiedName(QT, Ctx, Policy); - // RuntimeCallName = - // S.PP.getIdentifierInfo("caas__runtime__PrintValueRuntime__" + TypeStr); - // } - - // LookupResult R(S, RuntimeCallName, SourceLocation(),Sema::LookupOrdinaryName); - // S.LookupName(R, S.getCurScope()); - - if (llvm::Error E = BuildWrapperBody(R, S, Ctx, WrapperFD, V.getType(), ValPtr)) { + // 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 "{Unable to print the value!}"; + 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 (auto *Main = AddrOrErr->toPtr()) - return (*Main)(); + 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 = PrintQualType(Ctx, QT); + std::string QTStr = QualTypeToString(Ctx, QT); if (QT->isReferenceType()) QTStr += " &"; @@ -622,7 +564,8 @@ class InterfaceKindVisitor InterfaceKind VisitRecordType(const RecordType *Ty) { if (S.getLangOpts().CPlusPlus) return InterfaceKind::WithAlloc; - ExprResult AddrOfE = S.CreateBuiltinUnaryOp(SourceLocation(), UO_AddrOf, E->IgnoreImpCasts()); + 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; @@ -697,9 +640,9 @@ class InterfaceKindVisitor }; static constexpr llvm::StringRef VPName[] = { - "__clang_Interpreter_SetValueNoAlloc", - "__clang_Interpreter_SetValueWithAlloc", - "__clang_Interpreter_SetValueCopyArr", "__ci_newtag"}; + "__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. @@ -798,8 +741,9 @@ llvm::Expected Interpreter::convertExprToValue(Expr *E) { 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()); + return llvm::make_error( + "Cannot call to " + VPName[WithAlloc], + llvm::inconvertibleErrorCode()); TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ty, SourceLocation()); @@ -822,10 +766,11 @@ llvm::Expected Interpreter::convertExprToValue(Expr *E) { S.ActOnCallExpr(Scope, ValuePrintingInfo[InterfaceKind::CopyArray], SourceLocation(), Args, SourceLocation()); if (SetValueE.isInvalid()) - return llvm::make_error("Cannot call to " + VPName[CopyArray], - llvm::inconvertibleErrorCode()); + return llvm::make_error( + "Cannot call to " + VPName[CopyArray], + llvm::inconvertibleErrorCode()); break; - //return SetValueE.get(); + // return SetValueE.get(); } Expr *Args[] = {AllocCall.get(), ValuePrintingInfo[InterfaceKind::NewTag]}; ExprResult CXXNewCall = S.BuildCXXNew( @@ -837,8 +782,8 @@ llvm::Expected Interpreter::convertExprToValue(Expr *E) { if (CXXNewCall.isInvalid()) return llvm::make_error( - "Cannot build a call to placement new", - llvm::inconvertibleErrorCode()); + "Cannot build a call to placement new", + llvm::inconvertibleErrorCode()); SetValueE = S.ActOnFinishFullExpr(CXXNewCall.get(), /*DiscardedValue=*/false); diff --git a/clang/test/Interpreter/pretty-print.cpp b/clang/test/Interpreter/pretty-print.cpp index 87f595fd03360..6e15a8855db42 100644 --- a/clang/test/Interpreter/pretty-print.cpp +++ b/clang/test/Interpreter/pretty-print.cpp @@ -11,7 +11,7 @@ struct NonPOD { \ NonPOD(): I(sI++) {} \ }; namespace caas { namespace runtime { \ - const char* PrintValueRuntime(const NonPOD* type) { \ + 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"; \ @@ -27,6 +27,10 @@ NonPOD non_pod_arr[2][3]; 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]+}} @@ -148,7 +152,7 @@ struct MyDate { \ #include namespace caas { namespace runtime { \ - std::string PrintValueRuntime(const MyDate* d) { \ + std::string to_string(const MyDate* d) { \ std::string result; \ result = std::to_string(d->year) + '-'; \ switch(d->month) { default: result += "invalid month"; break; \ diff --git a/clang/tools/clang-repl/ClangRepl.cpp b/clang/tools/clang-repl/ClangRepl.cpp index f4efb5e1b49ee..fcdfdd2b475bf 100644 --- a/clang/tools/clang-repl/ClangRepl.cpp +++ b/clang/tools/clang-repl/ClangRepl.cpp @@ -231,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; }