https://github.com/vgvassilev updated https://github.com/llvm/llvm-project/pull/84769
>From a1639ef21a6085f12383e815ce2ed31976f78cfa Mon Sep 17 00:00:00 2001 From: Vassil Vassilev <v.g.vassi...@gmail.com> Date: Sun, 16 Jul 2023 21:18:26 +0000 Subject: [PATCH 1/2] [clang-repl] Implement value printing of custom types. Differential revision: https://reviews.llvm.org/D146809 --- clang/include/clang/Interpreter/Interpreter.h | 6 + .../Interpreter/PartialTranslationUnit.h | 2 + clang/include/clang/Interpreter/Value.h | 7 +- clang/lib/Headers/CMakeLists.txt | 1 + .../__clang_interpreter_runtime_printvalue.h | 261 ++++++++ clang/lib/Interpreter/CMakeLists.txt | 1 + clang/lib/Interpreter/DeviceOffload.cpp | 4 +- clang/lib/Interpreter/DeviceOffload.h | 4 +- clang/lib/Interpreter/IncrementalExecutor.cpp | 8 +- clang/lib/Interpreter/IncrementalExecutor.h | 7 +- clang/lib/Interpreter/IncrementalParser.cpp | 5 +- clang/lib/Interpreter/IncrementalParser.h | 5 +- clang/lib/Interpreter/Interpreter.cpp | 28 +- clang/lib/Interpreter/InterpreterUtils.cpp | 399 ++++++++++++- clang/lib/Interpreter/InterpreterUtils.h | 22 +- clang/lib/Interpreter/Value.cpp | 33 +- clang/lib/Interpreter/ValuePrinter.cpp | 564 ++++++++++++++++++ clang/test/Interpreter/pretty-print.cpp | 206 +++++++ clang/tools/clang-repl/CMakeLists.txt | 59 ++ clang/tools/clang-repl/ClangRepl.cpp | 21 +- .../Interpreter/CodeCompletionTest.cpp | 10 +- .../InterpreterExceptionTest.cpp | 5 +- .../IncrementalCompilerBuilderTest.cpp | 4 +- .../Interpreter/IncrementalProcessingTest.cpp | 5 +- .../Interpreter/InterpreterExtensionsTest.cpp | 9 +- .../unittests/Interpreter/InterpreterTest.cpp | 5 +- 26 files changed, 1627 insertions(+), 54 deletions(-) create mode 100644 clang/lib/Headers/__clang_interpreter_runtime_printvalue.h create mode 100644 clang/lib/Interpreter/ValuePrinter.cpp create mode 100644 clang/test/Interpreter/pretty-print.cpp diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h index 970e0245417b51..43331b0fe92c96 100644 --- a/clang/include/clang/Interpreter/Interpreter.h +++ b/clang/include/clang/Interpreter/Interpreter.h @@ -38,6 +38,8 @@ class ThreadSafeContext; namespace clang { class CompilerInstance; + +namespace caas { class IncrementalExecutor; class IncrementalParser; @@ -150,6 +152,7 @@ class Interpreter { llvm::Expected<llvm::orc::LLJIT &> getExecutionEngine(); llvm::Expected<PartialTranslationUnit &> Parse(llvm::StringRef Code); + llvm::Error ExecuteModule(std::unique_ptr<llvm::Module> &M); llvm::Error Execute(PartialTranslationUnit &T); llvm::Error ParseAndExecute(llvm::StringRef Code, Value *V = nullptr); llvm::Expected<llvm::orc::ExecutorAddr> CompileDtorCall(CXXRecordDecl *CXXRD); @@ -182,6 +185,8 @@ class Interpreter { Expr *SynthesizeExpr(Expr *E); + std::unique_ptr<llvm::Module> GenModule(); + private: size_t getEffectivePTUSize() const; void markUserCodeStart(); @@ -190,6 +195,7 @@ class Interpreter { llvm::SmallVector<Expr *, 4> ValuePrintingInfo; }; +} // namespace caas } // namespace clang #endif // LLVM_CLANG_INTERPRETER_INTERPRETER_H diff --git a/clang/include/clang/Interpreter/PartialTranslationUnit.h b/clang/include/clang/Interpreter/PartialTranslationUnit.h index bf91d559452b8a..d28cd30010cb92 100644 --- a/clang/include/clang/Interpreter/PartialTranslationUnit.h +++ b/clang/include/clang/Interpreter/PartialTranslationUnit.h @@ -24,6 +24,7 @@ namespace clang { class TranslationUnitDecl; +namespace caas { /// The class keeps track of various objects created as part of processing /// incremental inputs. struct PartialTranslationUnit { @@ -32,6 +33,7 @@ struct PartialTranslationUnit { /// The llvm IR produced for the input. std::unique_ptr<llvm::Module> TheModule; }; +} // namespace caas } // namespace clang #endif // LLVM_CLANG_INTERPRETER_PARTIALTRANSLATIONUNIT_H diff --git a/clang/include/clang/Interpreter/Value.h b/clang/include/clang/Interpreter/Value.h index d70e8f8719026b..6b737c05846fff 100644 --- a/clang/include/clang/Interpreter/Value.h +++ b/clang/include/clang/Interpreter/Value.h @@ -49,9 +49,11 @@ class raw_ostream; namespace clang { class ASTContext; -class Interpreter; class QualType; +namespace caas { +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 @@ -138,6 +140,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 +207,6 @@ template <> inline void *Value::as() const { return Data.m_Ptr; return (void *)as<uintptr_t>(); } - +} // namespace caas } // namespace clang #endif diff --git a/clang/lib/Headers/CMakeLists.txt b/clang/lib/Headers/CMakeLists.txt index e6ae4e19e81db9..15b34d33b01afd 100644 --- a/clang/lib/Headers/CMakeLists.txt +++ b/clang/lib/Headers/CMakeLists.txt @@ -34,6 +34,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 00000000000000..707eb0723f092d --- /dev/null +++ b/clang/lib/Headers/__clang_interpreter_runtime_printvalue.h @@ -0,0 +1,261 @@ +//===--- __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 + +#include <memory> +#include <string> +#include <tuple> +#include <type_traits> +#include <vector> + +// 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 <class T, + typename std::enable_if<!std::is_pointer<T>::value>::type * = nullptr> +inline std::string PrintValueRuntime(const T &) { + return "{not representable}"; +} + +#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(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(long long); +__DECL_PRINT_VALUE_RUNTIME(unsigned 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(char *const); +__DECL_PRINT_VALUE_RUNTIME(char *); +#endif + +namespace __repl_runtime_detail { + +// Custom void_t implementation for C++11 compatibility +template <typename... Ts> struct __repl_void_impl { typedef void type; }; + +// Helper to deduce the type of the expression 'std::begin(std::declval<T &>())' +template <typename T> +using __repl_begin_result = decltype(std::begin(std::declval<T &>())); + +// Helper to deduce the type of the expression 'std::end(std::declval<T &>())' +template <typename T> +using __repl_end_result = decltype(std::end(std::declval<T &>())); + +// Type trait to check if a type is iterable +template <typename T, typename = void> +struct __is_iterable : std::false_type {}; + +template <typename T> +struct __is_iterable<T, typename __repl_void_impl<__repl_begin_result<T>, + __repl_end_result<T>>::type> + : std::true_type {}; + +// Type trait to check if a type is std::pair +template <typename T> struct __is_pair : std::false_type {}; + +template <typename T, typename U> +struct __is_pair<std::pair<T, U>> : std::true_type {}; + +// Type trait to check if a type is std::map (or any associative container with +// mapped_type) +template <typename T, typename = void> struct __is_map : std::false_type {}; + +template <typename T> +struct __is_map<T, typename __repl_void_impl<typename T::mapped_type>::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<Elt>::value && __is_map<Container>::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 <typename Container, typename Elt, + typename std::enable_if<__is_pair<Elt>::value && + !__is_map<Container>::value, + bool>::type = true> +std::string __PrintCollectionElt(const Elt &Val) { + return TuplePairPrintValue(&Val); +} + +template <typename Container, typename Elt, + typename std::enable_if<!__is_pair<Elt>::value, bool>::type = true> +std::string __PrintCollectionElt(const Elt &Val) { + return PrintValueRuntime(&Val); +} + +template <class Tuple, std::size_t N = std::tuple_size<Tuple>(), + std::size_t TupleSize = std::tuple_size<Tuple>()> +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<EltNum>(*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<Tuple, Nm1>::print((const Tuple *)T); + return Str; + } +}; + +template <class Tuple, std::size_t TupleSize> +struct __TupleLikePrinter<Tuple, 0, TupleSize> { + static std::string print(const Tuple *T) { return ""; } +}; + +template <class T> inline std::string TuplePairPrintValue(const T *Val) { + std::string Str("{ "); + Str += __TupleLikePrinter<T>::print(Val); + Str += " }"; + return Str; +} + +struct __StdVectorBool { + bool Value; + __StdVectorBool(bool V) : Value(V) {} +}; +template <typename T> +std::string __PrintCollectionElt(const __StdVectorBool &Val) { + return PrintValueRuntime(&Val.Value); +} + +} // namespace __repl_runtime_detail + +template <typename Container, + typename std::enable_if< + __repl_runtime_detail::__is_iterable<Container>::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<Container>(*Beg); + } + Str += " }"; + return Str; +} + +template <typename T, size_t N> +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 <size_t N> inline std::string PrintValueRuntime(const char (*Obj)[N]) { + const auto *Str = reinterpret_cast<const char *const>(Obj); + return PrintValueRuntime(&Str); +} + +// std::vector<bool> +inline std::string PrintValueRuntime(const std::vector<bool> *Val) { + // Try our best to fix std::vector<bool> without too much of templated code. + std::vector<__repl_runtime_detail::__StdVectorBool> ValFixed(Val->begin(), + Val->end()); + return PrintValueRuntime<decltype(ValFixed)>(&ValFixed); +} + +// tuple +template <typename... Ts> +inline std::string PrintValueRuntime(const std::tuple<Ts...> *Val) { + using T = std::tuple<Ts...>; + return __repl_runtime_detail::TuplePairPrintValue<T>(Val); +} + +// pair +template <typename... Ts> +inline std::string PrintValueRuntime(const std::pair<Ts...> *Val) { + using T = std::pair<Ts...>; + return __repl_runtime_detail::TuplePairPrintValue<T>(Val); +} + +// unique_ptr +template <class T> +inline std::string PrintValueRuntime(const std::unique_ptr<T> *Val) { + auto Ptr = Val->get(); + return "std::unique_ptr -> " + PrintValueRuntime((const void **)&Ptr); +} + +// shared_ptr +template <class T> +inline std::string PrintValueRuntime(const std::shared_ptr<T> *Val) { + auto Ptr = Val->get(); + return "std::shared_ptr -> " + PrintValueRuntime((const void **)&Ptr); +} + +// weak_ptr +template <class T> +inline std::string PrintValueRuntime(const std::weak_ptr<T> *Val) { + auto Ptr = Val->lock().get(); + return "std::weak_ptr -> " + PrintValueRuntime((const void **)&Ptr); +} + +// string +template <class T> +inline std::string PrintValueRuntime(const std::basic_string<T> *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 9065f998f73c47..ea00bdc80f595d 100644 --- a/clang/lib/Interpreter/CMakeLists.txt +++ b/clang/lib/Interpreter/CMakeLists.txt @@ -20,6 +20,7 @@ add_clang_library(clangInterpreter Interpreter.cpp InterpreterUtils.cpp Value.cpp + ValuePrinter.cpp DEPENDS intrinsics_gen diff --git a/clang/lib/Interpreter/DeviceOffload.cpp b/clang/lib/Interpreter/DeviceOffload.cpp index fb42964e4936f2..9bbcce0ac0fe30 100644 --- a/clang/lib/Interpreter/DeviceOffload.cpp +++ b/clang/lib/Interpreter/DeviceOffload.cpp @@ -20,7 +20,7 @@ #include "llvm/MC/TargetRegistry.h" #include "llvm/Target/TargetMachine.h" -namespace clang { +namespace clang::caas { IncrementalCUDADeviceParser::IncrementalCUDADeviceParser( Interpreter &Interp, std::unique_ptr<CompilerInstance> Instance, @@ -173,4 +173,4 @@ llvm::Error IncrementalCUDADeviceParser::GenerateFatbinary() { IncrementalCUDADeviceParser::~IncrementalCUDADeviceParser() {} -} // namespace clang +} // namespace clang::caas diff --git a/clang/lib/Interpreter/DeviceOffload.h b/clang/lib/Interpreter/DeviceOffload.h index ce4f218c94c79d..204f85b872cf64 100644 --- a/clang/lib/Interpreter/DeviceOffload.h +++ b/clang/lib/Interpreter/DeviceOffload.h @@ -17,7 +17,7 @@ #include "llvm/Support/FileSystem.h" #include "llvm/Support/VirtualFileSystem.h" -namespace clang { +namespace clang::caas { class IncrementalCUDADeviceParser : public IncrementalParser { public: @@ -46,6 +46,6 @@ class IncrementalCUDADeviceParser : public IncrementalParser { llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> VFS; }; -} // namespace clang +} // namespace clang::caas #endif // LLVM_CLANG_LIB_INTERPRETER_DEVICE_OFFLOAD_H diff --git a/clang/lib/Interpreter/IncrementalExecutor.cpp b/clang/lib/Interpreter/IncrementalExecutor.cpp index 6f036107c14a9c..54f5d798384cb1 100644 --- a/clang/lib/Interpreter/IncrementalExecutor.cpp +++ b/clang/lib/Interpreter/IncrementalExecutor.cpp @@ -35,7 +35,7 @@ LLVM_ATTRIBUTE_USED void linkComponents() { << (void *)&llvm_orc_registerJITLoaderGDBAllocAction; } -namespace clang { +namespace clang::caas { llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>> IncrementalExecutor::createDefaultJITBuilder( @@ -68,6 +68,10 @@ IncrementalExecutor::IncrementalExecutor(llvm::orc::ThreadSafeContext &TSC, IncrementalExecutor::~IncrementalExecutor() {} +llvm::Error IncrementalExecutor::addModule(std::unique_ptr<llvm::Module> &M) { + return Jit->addIRModule({std::move(M), TSCtx}); +} + llvm::Error IncrementalExecutor::addModule(PartialTranslationUnit &PTU) { llvm::orc::ResourceTrackerSP RT = Jit->getMainJITDylib().createResourceTracker(); @@ -116,4 +120,4 @@ IncrementalExecutor::getSymbolAddress(llvm::StringRef Name, return SymOrErr->getAddress(); } -} // end namespace clang +} // namespace clang::caas diff --git a/clang/lib/Interpreter/IncrementalExecutor.h b/clang/lib/Interpreter/IncrementalExecutor.h index b4347209e14fe3..b6e7ddae401869 100644 --- a/clang/lib/Interpreter/IncrementalExecutor.h +++ b/clang/lib/Interpreter/IncrementalExecutor.h @@ -32,9 +32,11 @@ class ThreadSafeContext; namespace clang { -struct PartialTranslationUnit; class TargetInfo; +namespace caas { +struct PartialTranslationUnit; + class IncrementalExecutor { using CtorDtorIterator = llvm::orc::CtorDtorIterator; std::unique_ptr<llvm::orc::LLJIT> Jit; @@ -50,6 +52,7 @@ class IncrementalExecutor { llvm::orc::LLJITBuilder &JITBuilder, llvm::Error &Err); ~IncrementalExecutor(); + llvm::Error addModule(std::unique_ptr<llvm::Module> &M); llvm::Error addModule(PartialTranslationUnit &PTU); llvm::Error removeModule(PartialTranslationUnit &PTU); llvm::Error runCtors() const; @@ -62,7 +65,7 @@ class IncrementalExecutor { static llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>> createDefaultJITBuilder(llvm::orc::JITTargetMachineBuilder JTMB); }; - +} // namespace caas } // 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 ef90fe9e6f5451..0d96dbe77cdd3b 100644 --- a/clang/lib/Interpreter/IncrementalParser.cpp +++ b/clang/lib/Interpreter/IncrementalParser.cpp @@ -29,7 +29,7 @@ #include <sstream> -namespace clang { +namespace clang::caas { class IncrementalASTConsumer final : public ASTConsumer { Interpreter &Interp; @@ -414,4 +414,5 @@ llvm::StringRef IncrementalParser::GetMangledName(GlobalDecl GD) const { assert(CG); return CG->GetMangledName(GD); } -} // end namespace clang + +} // end namespace clang::caas diff --git a/clang/lib/Interpreter/IncrementalParser.h b/clang/lib/Interpreter/IncrementalParser.h index f63bce50acd3b9..46bf6aa4051f8e 100644 --- a/clang/lib/Interpreter/IncrementalParser.h +++ b/clang/lib/Interpreter/IncrementalParser.h @@ -31,9 +31,11 @@ namespace clang { class ASTConsumer; class CodeGenerator; class CompilerInstance; +class Parser; + +namespace caas { class IncrementalAction; class Interpreter; -class Parser; /// Provides support for incremental compilation. Keeps track of the state /// changes between the subsequent incremental input. /// @@ -91,6 +93,7 @@ class IncrementalParser { private: llvm::Expected<PartialTranslationUnit &> ParseOrWrapTopLevelDecl(); }; +} // namespace caas } // end namespace clang #endif // LLVM_CLANG_LIB_INTERPRETER_INCREMENTALPARSER_H diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp index b20e6efcebfd10..eb8b6592750467 100644 --- a/clang/lib/Interpreter/Interpreter.cpp +++ b/clang/lib/Interpreter/Interpreter.cpp @@ -43,7 +43,7 @@ #include "llvm/Support/raw_ostream.h" #include "llvm/TargetParser/Host.h" using namespace clang; - +using namespace clang::caas; // FIXME: Figure out how to unify with namespace init_convenience from // tools/clang-import-test/clang-import-test.cpp namespace { @@ -270,6 +270,7 @@ const char *const Runtimes = R"( __clang_Interpreter_SetValueCopyArr(Src[0], Placement, Size); } #endif // __cplusplus + #define __CLANG_REPL__ 1 )"; llvm::Expected<std::unique_ptr<Interpreter>> @@ -409,6 +410,23 @@ llvm::Error Interpreter::CreateExecutor() { void Interpreter::ResetExecutor() { IncrExecutor.reset(); } + +llvm::Error Interpreter::ExecuteModule(std::unique_ptr<llvm::Module> &M) { + if (!IncrExecutor) { + auto Err = CreateExecutor(); + if (Err) + return Err; + } + // FIXME: Add a callback to retain the llvm::Module once the JIT is done. + if (auto Err = IncrExecutor->addModule(M)) + return Err; + + if (auto Err = IncrExecutor->runCtors()) + return Err; + + return llvm::Error::success(); +} + llvm::Error Interpreter::Execute(PartialTranslationUnit &T) { assert(T.TheModule); if (!IncrExecutor) { @@ -532,6 +550,10 @@ Interpreter::CompileDtorCall(CXXRecordDecl *CXXRD) { return AddrOrErr; } +std::unique_ptr<llvm::Module> Interpreter::GenModule() { + return IncrParser->GenModule(); +} + static constexpr llvm::StringRef MagicRuntimeInterface[] = { "__clang_Interpreter_SetValueNoAlloc", "__clang_Interpreter_SetValueWithAlloc", @@ -589,8 +611,8 @@ class InterfaceKindVisitor llvm::SmallVector<Expr *, 3> Args; public: - InterfaceKindVisitor(ASTContext &Ctx, Sema &S, Expr *E) - : Ctx(Ctx), S(S), E(E) {} +InterfaceKindVisitor(ASTContext &Ctx, Sema &S, Expr *E) + : Ctx(Ctx), S(S), E(E) {} Interpreter::InterfaceKind VisitRecordType(const RecordType *Ty) { return Interpreter::InterfaceKind::WithAlloc; diff --git a/clang/lib/Interpreter/InterpreterUtils.cpp b/clang/lib/Interpreter/InterpreterUtils.cpp index 45f6322b8461ed..86838c671192f1 100644 --- a/clang/lib/Interpreter/InterpreterUtils.cpp +++ b/clang/lib/Interpreter/InterpreterUtils.cpp @@ -12,7 +12,7 @@ #include "InterpreterUtils.h" -namespace clang { +namespace clang::caas { IntegerLiteral *IntegerLiteralExpr(ASTContext &C, uint64_t Val) { return IntegerLiteral::Create(C, llvm::APSInt::getUnsigned(Val), @@ -81,7 +81,7 @@ NamedDecl *LookupNamed(Sema &S, llvm::StringRef Name, else { const DeclContext *PrimaryWithin = nullptr; if (const auto *TD = dyn_cast<TagDecl>(Within)) - PrimaryWithin = llvm::dyn_cast_or_null<DeclContext>(TD->getDefinition()); + PrimaryWithin = dyn_cast_if_present<DeclContext>(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<NamedDecl>(R.getFoundDecl()); + return dyn_cast<NamedDecl>(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<NamespaceDecl>(DC)) { + while (NS && NS->isInline()) { + // Ignore inline namespace; + NS = dyn_cast_if_present<NamespaceDecl>(NS->getDeclContext()); + } + if (NS && NS->getDeclName()) + return CreateNestedNameSpecifier(Ctx, NS); + return nullptr; // no starting '::', no anonymous + } + if (const auto *TD = dyn_cast<TagDecl>(DC)) + return CreateNestedNameSpecifier(Ctx, TD, FullyQualify); + if (const auto *TDD = dyn_cast<TypedefNameDecl>(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<NamedDecl>(D->getDeclContext()); + const auto *OuterNs = dyn_cast_if_present<NamespaceDecl>(D->getDeclContext()); + if (Outer && !(OuterNs && OuterNs->isAnonymousNamespace())) { + + if (const auto *CXXD = dyn_cast<CXXRecordDecl>(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<NamedDecl>(D); + OuterNs = dyn_cast<NamespaceDecl>(D); + } + } + } + + if (OuterNs) + return CreateNestedNameSpecifier(Ctx, OuterNs); + + if (const auto *TD = dyn_cast<TagDecl>(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<TypedefType>(TypePtr)) { + D = TDT->getDecl(); + } else { + // There are probably other cases ... + if (const auto *TT = dyn_cast_if_present<TagType>(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<TagType>(Type)) + TD = TT->getDecl(); + else + TD = Type->getAsCXXRecordDecl(); + + if (TD) + return CreateNestedNameSpecifier(Ctx, TD, true /*FullyQualified*/); + + if (const auto *TDD = dyn_cast<TypedefType>(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<TemplateArgument, 2> 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<ASTContext &>(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<const TemplateSpecializationType>(Typeptr)) { + + bool MightHaveChanged = false; + llvm::SmallVector<TemplateArgument, 4> 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<const RecordType>(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<ClassTemplateSpecializationDecl>(TSTRecord->getDecl())) { + const TemplateArgumentList &TemplateArgs = TSTdecl->getTemplateArgs(); + + bool MightHaveChanged = false; + llvm::SmallVector<TemplateArgument, 4> 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<NamespaceDecl>(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<PointerType>(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<ReferenceType>(QT.getTypePtr())) { + // Get the qualifiers. + bool IsLValueRefTy = isa<LValueReferenceType>(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<AutoType>(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<SubstTemplateTypeParmType>(QT.getTypePtr())) { + // Get the qualifiers. + Qualifiers Quals = QT.getQualifiers(); + + QT = cast<SubstTemplateTypeParmType>(QT.getTypePtr())->desugar(); + + // Add back the qualifiers. + QT = Ctx.getQualifiedType(QT, Quals); + } + + NestedNameSpecifier *Prefix = nullptr; + Qualifiers PrefixQualifiers; + if (const auto *EType = dyn_cast<ElaboratedType>(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<const TemplateSpecializationType>(QT.getTypePtr())) { + + Qualifiers Qualifiers = QT.getLocalQualifiers(); + const Type *TypePtr = GetFullyQualifiedLocalType(Ctx, QT.getTypePtr()); + QT = Ctx.getQualifiedType(TypePtr, Qualifiers); + + } else if (isa<const RecordType>(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 +} // namespace clang::caas diff --git a/clang/lib/Interpreter/InterpreterUtils.h b/clang/lib/Interpreter/InterpreterUtils.h index 8df158c17d4919..61c1cab822208c 100644 --- a/clang/lib/Interpreter/InterpreterUtils.h +++ b/clang/lib/Interpreter/InterpreterUtils.h @@ -33,7 +33,7 @@ #include "llvm/Support/Errc.h" #include "llvm/TargetParser/Host.h" -namespace clang { +namespace clang::caas { IntegerLiteral *IntegerLiteralExpr(ASTContext &C, uint64_t Val); Expr *CStyleCastPtrExpr(Sema &S, QualType Ty, Expr *E); @@ -48,7 +48,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); -} // namespace clang + +class Value; + +std::string ReplPrintTypeImpl(const Value &); +std::string ReplPrintDataImpl(const Value &); +} // namespace clang::caas #endif diff --git a/clang/lib/Interpreter/Value.cpp b/clang/lib/Interpreter/Value.cpp index eb2ce9c9fd3302..50e3fa3ff6fadc 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,9 @@ #include <cstdint> #include <utility> +using namespace clang; +using namespace clang::caas; + namespace { // This is internal buffer maintained by Value, used to hold temporaries. @@ -231,6 +235,11 @@ void *Value::getPtr() const { return Data.m_Ptr; } +void **Value::getPtrAddress() const { + assert(ValueKind == K_PtrOrObj); + return &const_cast<Value *>(this)->Data.m_Ptr; +} + QualType Value::getType() const { return QualType::getFromOpaquePtr(OpaqueType); } @@ -256,14 +265,32 @@ const ASTContext &Value::getASTContext() const { void Value::dump() const { print(llvm::outs()); } void Value::printType(llvm::raw_ostream &Out) const { - Out << "Not implement yet.\n"; + Out << ReplPrintTypeImpl(*this); } + void Value::printData(llvm::raw_ostream &Out) const { - Out << "Not implement yet.\n"; + Out << ReplPrintDataImpl(*this); } +// 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) const { 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/Interpreter/ValuePrinter.cpp b/clang/lib/Interpreter/ValuePrinter.cpp new file mode 100644 index 00000000000000..565b170b04e29d --- /dev/null +++ b/clang/lib/Interpreter/ValuePrinter.cpp @@ -0,0 +1,564 @@ +//===--- ValuePrinter.cpp - Utils for value printing --------*- 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 value printing in clang-repl. +// +//===----------------------------------------------------------------------===// +#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 <cassert> +#include <string> + +using namespace clang; +using namespace clang::caas; + +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) { + std::string Str; + llvm::raw_string_ostream SS(Str); + PrintingPolicy Policy(Ctx.getPrintingPolicy()); + // Print the Allocator in STL containers, for instance. + Policy.SuppressDefaultTemplateArgs = false; + Policy.SuppressUnwrittenScope = true; + // Print 'a<b<c> >' rather than 'a<b<c>>'. + 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<TagType>(NonRefTy)) + SS << PrintDeclType(NonRefTy, TTy->getDecl()); + else if (const auto *TRy = dyn_cast<RecordType>(NonRefTy)) + SS << PrintDeclType(NonRefTy, TRy->getDecl()); + else { + const QualType Canon = NonRefTy.getCanonicalType(); + if (Canon->isBuiltinType() && !NonRefTy->isFunctionPointerType() && + !NonRefTy->isMemberPointerType()) { + SS << Canon.getAsString(Ctx.getPrintingPolicy()); + } else if (const auto *TDTy = dyn_cast<TypedefType>(NonRefTy)) { + // FIXME: TemplateSpecializationType & SubstTemplateTypeParmType checks + // are predominately to get STL containers to print nicer and might be + // better handled in GetFullyQualifiedName. + // + // std::vector<Type>::iterator is a TemplateSpecializationType + // std::vector<Type>::value_type is a SubstTemplateTypeParmType + // + QualType SSDesugar = TDTy->getLocallyUnqualifiedSingleStepDesugaredType(); + if (llvm::isa<SubstTemplateTypeParmType>(SSDesugar)) + SS << GetFullTypeName(Ctx, Canon); + else if (llvm::isa<TemplateSpecializationType>(SSDesugar)) + SS << GetFullTypeName(Ctx, NonRefTy); + else + SS << PrintDeclType(NonRefTy, TDTy->getDecl()); + } else + SS << GetFullTypeName(Ctx, NonRefTy); + } + + if (QT->isReferenceType()) + SS << " &"; + + return Str; +} + +static std::string PrintEnum(const Value &V) { + std::string Str; + llvm::raw_string_ostream SS(Str); + ASTContext &Ctx = const_cast<ASTContext &>(V.getASTContext()); + + QualType DesugaredTy = V.getType().getDesugaredType(Ctx); + const EnumType *EnumTy = DesugaredTy.getNonReferenceType()->getAs<EnumType>(); + 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<24> 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<TopLevelStmtDecl>(*Decls.begin()); + + // Get __clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void + // *OpaqueType, void *Val); + if (auto *InterfaceCall = llvm::dyn_cast<CallExpr>(TLSD->getStmt())) { + const auto *Arg = InterfaceCall->getArg(/*Val*/ 3); + // Get rid of cast nodes. + while (const CastExpr *CastE = llvm::dyn_cast<CastExpr>(Arg)) + Arg = CastE->getSubExpr(); + if (const DeclRefExpr *DeclRefExp = llvm::dyn_cast<DeclRefExpr>(Arg)) + FD = llvm::dyn_cast<FunctionDecl>(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 gonnd 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 = + S.PP.getIdentifierInfo("PrintValueRuntime"); + clang::LookupResult R(S, RuntimeCallName, SourceLocation(), + clang::Sema::LookupOrdinaryName); + S.LookupQualifiedName(R, Ctx.getTranslationUnitDecl()); + + Expr *OverldExpr = UnresolvedLookupExpr::Create( + Ctx, /*NamingClass=*/nullptr, NestedNameSpecifierLoc(), + clang::DeclarationNameInfo(RuntimeCallName, SourceLocation()), + /*RequiresADL*/ false, R.isOverloadedResult(), R.begin(), R.end()); + + if (const auto *AT = llvm::dyn_cast<AutoType>(QT.getTypePtr())) { + if (AT->isDeduced()) + QT = AT->getDeducedType().getDesugaredType(Ctx); + } + + if (const auto *PT = llvm::dyn_cast<PointerType>(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<ReferenceType>(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<Expr *, 1> 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<llvm::orc::ExecutorAddr> 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(); + Consumer.HandleTranslationUnit(Interp.getASTContext()); + + if (std::unique_ptr<llvm::Module> M = Interp.GenModule()) { + if (llvm::Error Err = Interp.ExecuteModule(M)) + return Err; + ASTNameGenerator ASTNameGen(Interp.getASTContext()); + llvm::Expected<llvm::orc::ExecutorAddr> 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; +} + +static std::string SynthesizeRuntimePrint(const Value &V) { + Interpreter &Interp = const_cast<Interpreter &>(V.getInterpreter()); + Sema &S = Interp.getCompilerInstance()->getSema(); + ASTContext &Ctx = S.getASTContext(); + + // Only include this header once and on demand. Because it's very heavy. + static bool Included = false; + if (!Included) { + Included = true; + llvm::cantFail( + Interp.Parse("#include <__clang_interpreter_runtime_printvalue.h>")); + } + // Lookup 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<TypeDecl>(StdStringDecl); + assert(StdStringTyDecl && "Cannot find type of std::string"); + + // Create the wrapper function. + DeclarationName DeclName = &Ctx.Idents.get(CreateUniqName(WrapperName)); + QualType RetTy(StdStringTyDecl->getTypeForDecl(), 0); + 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(); + if (!V.isManuallyAlloc()) + ValPtr = V.getPtrAddress(); + + BuildWrapperBody(Interp, S, Ctx, WrapperFD, V.getType(), ValPtr); + + auto AddrOrErr = CompileDecl(Interp, WrapperFD); + if (!AddrOrErr) + llvm::logAllUnhandledErrors(AddrOrErr.takeError(), llvm::errs(), + "Fail to get symbol address"); + if (auto *Main = AddrOrErr->toPtr<std::string (*)()>()) + return (*Main)(); + return "Error to print the value!"; +} + +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); +} + +template <typename T> static std::string PrintValueWrapper(const T &Val) { + return PrintValueRuntime(&Val); +} + +namespace clang::caas { +std::string ReplPrintDataImpl(const Value &V) { + std::string Str; + llvm::raw_string_ostream SS(Str); + + QualType QT = V.getType(); + QualType DesugaredTy = QT.getDesugaredType(V.getASTContext()); + QualType NonRefTy = DesugaredTy.getNonReferenceType(); + + if (NonRefTy->isNullPtrType()) + SS << "nullptr\n"; + else if (NonRefTy->isEnumeralType()) + return PrintEnum(V); + else if (NonRefTy->isFunctionType()) + return PrintFunction(V, &V); + else if ((NonRefTy->isPointerType() || NonRefTy->isMemberPointerType()) && + NonRefTy->getPointeeType()->isFunctionProtoType()) + return PrintFunction(V, V.getPtr()); + else if (auto *BT = DesugaredTy.getCanonicalType()->getAs<BuiltinType>()) { + switch (BT->getKind()) { + case BuiltinType::Bool: { + SS << PrintValueWrapper(V.getBool()); + break; + } + case BuiltinType::Char_S: + case BuiltinType::SChar: { + SS << PrintValueWrapper(V.getSChar()); + break; + } + case BuiltinType::Short: { + SS << PrintValueWrapper(V.getShort()); + break; + } + case BuiltinType::Int: { + SS << PrintValueWrapper(V.getInt()); + break; + } + case BuiltinType::Long: { + SS << PrintValueWrapper(V.getLong()); + break; + } + case BuiltinType::LongLong: { + SS << PrintValueWrapper(V.getLongLong()); + break; + } + case BuiltinType::Char_U: + case BuiltinType::UChar: { + SS << PrintValueWrapper(V.getUChar()); + break; + } + case BuiltinType::UShort: { + SS << PrintValueWrapper(V.getUShort()); + break; + } + case BuiltinType::UInt: { + SS << PrintValueWrapper(V.getUInt()); + break; + } + case BuiltinType::ULong: { + SS << PrintValueWrapper(V.getULong()); + break; + } + case BuiltinType::ULongLong: { + SS << PrintValueWrapper(V.getULongLong()); + break; + } + case BuiltinType::Float: { + SS << PrintValueWrapper(V.getFloat()); + break; + } + case BuiltinType::Double: { + SS << PrintValueWrapper(V.getDouble()); + break; + } + case BuiltinType::LongDouble: { + SS << PrintValueWrapper(V.getLongDouble()); + break; + } + default: + llvm_unreachable("Unknown Builtintype kind"); + } + } else if (auto *CXXRD = NonRefTy->getAsCXXRecordDecl(); + CXXRD && CXXRD->isLambda()) { + SS << PrintAddress(V.getPtr(), '@'); + } else { + // All fails then generate a runtime call, this is slow. + SS << SynthesizeRuntimePrint(V); + } + return Str; +} + +std::string ReplPrintTypeImpl(const Value &V) { + ASTContext &Ctx = const_cast<ASTContext &>(V.getASTContext()); + QualType QT = V.getType(); + + return PrintQualType(Ctx, QT); +} +} // namespace clang::caas diff --git a/clang/test/Interpreter/pretty-print.cpp b/clang/test/Interpreter/pretty-print.cpp new file mode 100644 index 00000000000000..6a489d3550aa0b --- /dev/null +++ b/clang/test/Interpreter/pretty-print.cpp @@ -0,0 +1,206 @@ +// 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*,...); + +char c = 'a'; +c +// CHECK: (char) 'a' + +struct NonPOD { \ + static int sI; \ + int I; \ + NonPOD(): I(sI++) {} \ +}; +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-NEXT: (NonPOD[2][3]) { { 0, 1, 2 }, { 3, 4, 5 } } + +const char* 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; +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 + +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 + +// Arrays. + +int arr[3] = {1,2,3}; +arr +// CHECK-NEXT: (int[3]) { 1, 2, 3 } + + +// 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<int n> struct F{ enum {RET=F<n-1>::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 + +int foo() { return 42; } +foo() +// CHECK-NEXT: (int) 42 + +void bar() {} +bar() + +struct S5 { int foo() { return 42; }}; +&S5::foo +// CHECK-NEXT: (int (S5::*)()) Function @0x{{[0-9a-f]+}} + +#include <memory> + +auto p1 = std::make_shared<int>(42); +p1 +// CHECK-NEXT: (std::shared_ptr<int> &) std::shared_ptr -> @0x{{[0-9a-f]+}} + +std::unique_ptr<int> p2(new int(42)); +p2 +// CHECK-NEXT: (std::unique_ptr<int> &) std::unique_ptr -> @0x{{[0-9a-f]+}} + +#include <array> +std::array<int, 3> a{1, 2, 3}; +a +// CHECK-NEXT: (std::array<int, 3> &) { 1, 2, 3 } + +#include <vector> +std::vector<int> v1 = {7, 5, 16, 8}; +v1 +// CHECK-NEXT: (std::vector<int> &) { 7, 5, 16, 8 } + +std::vector<bool> v = {true, false, true}; +v +// CHECK-NEXT: (std::vector<bool> &) { true, false, true } + +#include <deque> +std::deque<int> dq = {7, 5, 16, 8}; +dq +// CHECK-NEXT: (std::deque<int> &) { 7, 5, 16, 8 } + +#include <forward_list> +std::forward_list<int> fl {3,4,5,6}; +fl +// CHECK-NEXT: (std::forward_list<int> &) { 3, 4, 5, 6 } + +#include <set> +std::set<int> z1 = {2,4,6,8}; +z1 +// CHECK-NEXT: (std::set<int> &) { 2, 4, 6, 8 } + +#include <unordered_set> +std::unordered_set<int> z2 = {8,2,4,6}; +z2 +// CHECK-NEXT: (std::unordered_set<int> &) { [[Num:[0-9]+]], [[Num:[0-9]+]], [[Num:[0-9]+]], [[Num:[0-9]+]] } + +std::multiset<int> e {3,2,1,2,4,7,3}; +e +// CHECK-NEXT: (std::multiset<int> &) { 1, 2, 2, 3, 3, 4, 7 } + +#include <string> +std::string std_str = "Hello, world!"; +std_str +// CHECK-NEXT: (std::string &) "Hello, world!" + +#include <utility> +std::pair<int,char> pr(42,'a'); +pr +// CHECK-NEXT: (std::pair<int, char> &) { 42, 'a' } + +#include <tuple> +std::tuple<int,double,char> tu(42,3.14,'a'); +tu +// CHECK-NEXT: (std::tuple<int, double, char> &) { 42, 3.14000000000, 'a' } + +#include <map> +std::map<const char*, int> m1{{"CPU", 10}, {"GPU", 15}, {"RAM", 20}}; +m1 +// CHECK-NEXT: (std::map<const char *, int> &) { "CPU" => 10, "GPU" => 15, "RAM" => 20 } + +#include <unordered_map> +std::unordered_map<int, int> m2 = { {1,2}, {3,4}}; +m2 +// CHECK-NEXT: (std::unordered_map<int, int> &) { [[Num:[0-9]+]] => [[Num:[0-9]+]], [[Num:[0-9]+]] => [[Num:[0-9]+]] } + +struct MyType {}; +std::string PrintValueRuntime(const MyType*) { return "My pretty printer!"; } +MyType{} +// CHECK-NEXT: (MyType) My pretty printer! +%quit + diff --git a/clang/tools/clang-repl/CMakeLists.txt b/clang/tools/clang-repl/CMakeLists.txt index d3dec1984b78d2..2b7d5f01418919 100644 --- a/clang/tools/clang-repl/CMakeLists.txt +++ b/clang/tools/clang-repl/CMakeLists.txt @@ -11,6 +11,65 @@ add_clang_tool(clang-repl ClangRepl.cpp ) +if(MSVC) + set_target_properties(clang-repl PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS 1) + + # RTTI/C++ symbols + set(clang_repl_exports ${clang_repl_exports} ??_7type_info@@6B@ + ?__type_info_root_node@@3U__type_info_node@@A + ?nothrow@std@@3Unothrow_t@1@B + ) + + # Compiler added symbols for static variables. NOT for VStudio < 2015 + set(clang_repl_exports ${clang_repl_exports} _Init_thread_abort _Init_thread_epoch + _Init_thread_footer _Init_thread_header _tls_index + ) + + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + # new/delete variants needed when linking to static msvc runtime (esp. Debug) + set(clang_repl_exports ${clang_repl_exports} + ??2@YAPEAX_K@Z + ??3@YAXPEAX@Z + ??_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} + ??2@YAPAXI@Z + ??3@YAXPAX@Z + ??3@YAXPAXI@Z + ??_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() + + # List to '/EXPORT:sym0 /EXPORT:sym1 /EXPORT:sym2 ...' + foreach(sym ${clang_repl_exports}) + set(clang_repl_link_str "${clang_repl_link_str} /EXPORT:${sym}") + endforeach(sym ${clang_repl_exports}) + + set_property(TARGET clang-repl APPEND_STRING PROPERTY LINK_FLAGS ${clang_repl_link_str}) + +endif(MSVC) + clang_target_link_libraries(clang-repl PRIVATE clangAST clangBasic diff --git a/clang/tools/clang-repl/ClangRepl.cpp b/clang/tools/clang-repl/ClangRepl.cpp index aecf61b97fc719..022ab35c93312c 100644 --- a/clang/tools/clang-repl/ClangRepl.cpp +++ b/clang/tools/clang-repl/ClangRepl.cpp @@ -82,10 +82,10 @@ static int checkDiagErrors(const clang::CompilerInstance *CI, bool HasError) { } struct ReplListCompleter { - clang::IncrementalCompilerBuilder &CB; - clang::Interpreter &MainInterp; - ReplListCompleter(clang::IncrementalCompilerBuilder &CB, - clang::Interpreter &Interp) + clang::caas::IncrementalCompilerBuilder &CB; + clang::caas::Interpreter &MainInterp; + ReplListCompleter(clang::caas::IncrementalCompilerBuilder &CB, + clang::caas::Interpreter &Interp) : CB(CB), MainInterp(Interp){}; std::vector<llvm::LineEditor::Completion> operator()(llvm::StringRef Buffer, @@ -117,7 +117,7 @@ ReplListCompleter::operator()(llvm::StringRef Buffer, size_t Pos, size_t Lines = std::count(Buffer.begin(), std::next(Buffer.begin(), Pos), '\n') + 1; - auto Interp = clang::Interpreter::create(std::move(*CI)); + auto Interp = clang::caas::Interpreter::create(std::move(*CI)); if (auto Err = Interp.takeError()) { // log the error and returns an empty vector; @@ -165,7 +165,7 @@ int main(int argc, const char **argv) { return 0; } - clang::IncrementalCompilerBuilder CB; + clang::caas::IncrementalCompilerBuilder CB; CB.SetCompilerArgs(ClangArgv); std::unique_ptr<clang::CompilerInstance> DeviceCI; @@ -200,11 +200,10 @@ int main(int argc, const char **argv) { if (CudaEnabled) DeviceCI->LoadRequestedPlugins(); - std::unique_ptr<clang::Interpreter> Interp; - + std::unique_ptr<clang::caas::Interpreter> Interp; if (CudaEnabled) { - Interp = ExitOnErr( - clang::Interpreter::createWithCUDA(std::move(CI), std::move(DeviceCI))); + Interp = ExitOnErr(clang::caas::Interpreter::createWithCUDA( + std::move(CI), std::move(DeviceCI))); if (CudaPath.empty()) { ExitOnErr(Interp->LoadDynamicLibrary("libcudart.so")); @@ -213,7 +212,7 @@ int main(int argc, const char **argv) { ExitOnErr(Interp->LoadDynamicLibrary(CudaRuntimeLibPath.c_str())); } } else - Interp = ExitOnErr(clang::Interpreter::create(std::move(CI))); + Interp = ExitOnErr(clang::caas::Interpreter::create(std::move(CI))); for (const std::string &input : OptInputs) { if (auto Err = Interp->ParseAndExecute(input)) diff --git a/clang/unittests/Interpreter/CodeCompletionTest.cpp b/clang/unittests/Interpreter/CodeCompletionTest.cpp index 873fbda32f0579..f487530f66a778 100644 --- a/clang/unittests/Interpreter/CodeCompletionTest.cpp +++ b/clang/unittests/Interpreter/CodeCompletionTest.cpp @@ -13,14 +13,14 @@ using namespace clang; namespace { -auto CB = clang::IncrementalCompilerBuilder(); +auto CB = clang::caas::IncrementalCompilerBuilder(); -static std::unique_ptr<Interpreter> createInterpreter() { +static std::unique_ptr<clang::caas::Interpreter> createInterpreter() { auto CI = cantFail(CB.CreateCpp()); - return cantFail(clang::Interpreter::create(std::move(CI))); + return cantFail(clang::caas::Interpreter::create(std::move(CI))); } -static std::vector<std::string> runComp(clang::Interpreter &MainInterp, +static std::vector<std::string> runComp(clang::caas::Interpreter &MainInterp, llvm::StringRef Input, llvm::Error &ErrR) { auto CI = CB.CreateCpp(); @@ -29,7 +29,7 @@ static std::vector<std::string> runComp(clang::Interpreter &MainInterp, return {}; } - auto Interp = clang::Interpreter::create(std::move(*CI)); + auto Interp = clang::caas::Interpreter::create(std::move(*CI)); if (auto Err = Interp.takeError()) { // log the error and returns an empty vector; ErrR = std::move(Err); diff --git a/clang/unittests/Interpreter/ExceptionTests/InterpreterExceptionTest.cpp b/clang/unittests/Interpreter/ExceptionTests/InterpreterExceptionTest.cpp index 2f1c4efb381f00..be9c5f7c01a517 100644 --- a/clang/unittests/Interpreter/ExceptionTests/InterpreterExceptionTest.cpp +++ b/clang/unittests/Interpreter/ExceptionTests/InterpreterExceptionTest.cpp @@ -38,6 +38,7 @@ LLVM_ATTRIBUTE_USED int __lsan_is_turned_off() { return 1; } #endif using namespace clang; +using namespace clang::caas; namespace { using Args = std::vector<const char *>; @@ -46,12 +47,12 @@ createInterpreter(const Args &ExtraArgs = {}, DiagnosticConsumer *Client = nullptr) { Args ClangArgs = {"-Xclang", "-emit-llvm-only"}; ClangArgs.insert(ClangArgs.end(), ExtraArgs.begin(), ExtraArgs.end()); - auto CB = clang::IncrementalCompilerBuilder(); + auto CB = clang::caas::IncrementalCompilerBuilder(); CB.SetCompilerArgs(ClangArgs); auto CI = cantFail(CB.CreateCpp()); if (Client) CI->getDiagnostics().setClient(Client, /*ShouldOwnClient=*/false); - return cantFail(clang::Interpreter::create(std::move(CI))); + return cantFail(clang::caas::Interpreter::create(std::move(CI))); } TEST(InterpreterTest, CatchException) { diff --git a/clang/unittests/Interpreter/IncrementalCompilerBuilderTest.cpp b/clang/unittests/Interpreter/IncrementalCompilerBuilderTest.cpp index f729566f7efde6..4e9b0270a80a4e 100644 --- a/clang/unittests/Interpreter/IncrementalCompilerBuilderTest.cpp +++ b/clang/unittests/Interpreter/IncrementalCompilerBuilderTest.cpp @@ -29,7 +29,7 @@ static void cleanupRemappedFileBuffers(CompilerInstance &CI) { TEST(IncrementalCompilerBuilder, SetCompilerArgs) { std::vector<const char *> ClangArgv = {"-Xclang", "-ast-dump-all"}; - auto CB = clang::IncrementalCompilerBuilder(); + auto CB = clang::caas::IncrementalCompilerBuilder(); CB.SetCompilerArgs(ClangArgv); auto CI = cantFail(CB.CreateCpp()); EXPECT_TRUE(CI->getFrontendOpts().ASTDumpAll); @@ -37,7 +37,7 @@ TEST(IncrementalCompilerBuilder, SetCompilerArgs) { } TEST(IncrementalCompilerBuilder, SetTargetTriple) { - auto CB = clang::IncrementalCompilerBuilder(); + auto CB = clang::caas::IncrementalCompilerBuilder(); CB.SetTargetTriple("armv6-none-eabi"); auto CI = cantFail(CB.CreateCpp()); EXPECT_EQ(CI->getTargetOpts().Triple, "armv6-none-unknown-eabi"); diff --git a/clang/unittests/Interpreter/IncrementalProcessingTest.cpp b/clang/unittests/Interpreter/IncrementalProcessingTest.cpp index accdf682896340..7f64c5378a21fb 100644 --- a/clang/unittests/Interpreter/IncrementalProcessingTest.cpp +++ b/clang/unittests/Interpreter/IncrementalProcessingTest.cpp @@ -27,6 +27,7 @@ using namespace llvm; using namespace clang; +using namespace clang::caas; namespace { @@ -52,12 +53,12 @@ const Function *getGlobalInit(llvm::Module *M) { TEST(IncrementalProcessing, EmitCXXGlobalInitFunc) { std::vector<const char *> ClangArgv = {"-Xclang", "-emit-llvm-only"}; - auto CB = clang::IncrementalCompilerBuilder(); + auto CB = clang::caas::IncrementalCompilerBuilder(); CB.SetCompilerArgs(ClangArgv); auto CI = cantFail(CB.CreateCpp()); auto Interp = llvm::cantFail(Interpreter::create(std::move(CI))); - std::array<clang::PartialTranslationUnit *, 2> PTUs; + std::array<clang::caas::PartialTranslationUnit *, 2> PTUs; PTUs[0] = &llvm::cantFail(Interp->Parse(TestProgram1)); ASSERT_TRUE(PTUs[0]->TheModule); diff --git a/clang/unittests/Interpreter/InterpreterExtensionsTest.cpp b/clang/unittests/Interpreter/InterpreterExtensionsTest.cpp index b971cd550dc507..47a9600e99f910 100644 --- a/clang/unittests/Interpreter/InterpreterExtensionsTest.cpp +++ b/clang/unittests/Interpreter/InterpreterExtensionsTest.cpp @@ -35,6 +35,7 @@ #endif using namespace clang; +using namespace clang::caas; namespace { static bool HostSupportsJit() { @@ -104,7 +105,7 @@ TEST(InterpreterExtensionsTest, ExecutorCreateReset) { if (!HostSupportsJit()) GTEST_SKIP(); - clang::IncrementalCompilerBuilder CB; + clang::caas::IncrementalCompilerBuilder CB; llvm::Error ErrOut = llvm::Error::success(); TestCreateResetExecutor Interp(cantFail(CB.CreateCpp()), ErrOut); cantFail(std::move(ErrOut)); @@ -156,7 +157,7 @@ class RecordRuntimeIBMetrics : public Interpreter { }; TEST(InterpreterExtensionsTest, FindRuntimeInterface) { - clang::IncrementalCompilerBuilder CB; + clang::caas::IncrementalCompilerBuilder CB; llvm::Error ErrOut = llvm::Error::success(); RecordRuntimeIBMetrics Interp(cantFail(CB.CreateCpp()), ErrOut); cantFail(std::move(ErrOut)); @@ -203,7 +204,7 @@ TEST(InterpreterExtensionsTest, DefaultCrossJIT) { if (!IsARMTargetRegistered()) GTEST_SKIP(); - IncrementalCompilerBuilder CB; + clang::caas::IncrementalCompilerBuilder CB; CB.SetTargetTriple("armv6-none-eabi"); auto CI = cantFail(CB.CreateCpp()); llvm::Error ErrOut = llvm::Error::success(); @@ -222,7 +223,7 @@ TEST(InterpreterExtensionsTest, CustomCrossJIT) { std::string TargetTriple = "armv6-none-eabi"; - IncrementalCompilerBuilder CB; + clang::caas::IncrementalCompilerBuilder CB; CB.SetTargetTriple(TargetTriple); auto CI = cantFail(CB.CreateCpp()); llvm::Error ErrOut = llvm::Error::success(); diff --git a/clang/unittests/Interpreter/InterpreterTest.cpp b/clang/unittests/Interpreter/InterpreterTest.cpp index 69bc2da242884e..53c51809ca7319 100644 --- a/clang/unittests/Interpreter/InterpreterTest.cpp +++ b/clang/unittests/Interpreter/InterpreterTest.cpp @@ -29,6 +29,7 @@ #include "gtest/gtest.h" using namespace clang; +using namespace caas; #if defined(_AIX) #define CLANG_INTERPRETER_NO_SUPPORT_EXEC @@ -46,12 +47,12 @@ createInterpreter(const Args &ExtraArgs = {}, DiagnosticConsumer *Client = nullptr) { Args ClangArgs = {"-Xclang", "-emit-llvm-only"}; ClangArgs.insert(ClangArgs.end(), ExtraArgs.begin(), ExtraArgs.end()); - auto CB = clang::IncrementalCompilerBuilder(); + auto CB = clang::caas::IncrementalCompilerBuilder(); CB.SetCompilerArgs(ClangArgs); auto CI = cantFail(CB.CreateCpp()); if (Client) CI->getDiagnostics().setClient(Client, /*ShouldOwnClient=*/false); - return cantFail(clang::Interpreter::create(std::move(CI))); + return cantFail(clang::caas::Interpreter::create(std::move(CI))); } static size_t DeclsSize(TranslationUnitDecl *PTUDecl) { >From 8a5190882a8863badefe302a45580c3ef0704abb Mon Sep 17 00:00:00 2001 From: Vassil Vassilev <v.g.vassi...@gmail.com> Date: Thu, 20 Jul 2023 19:51:25 +0000 Subject: [PATCH 2/2] Store state --- clang/include/clang/AST/ASTContext.h | 2 ++ clang/include/clang/Interpreter/Interpreter.h | 10 ++++++++++ clang/lib/Interpreter/IncrementalExecutor.cpp | 4 ---- clang/lib/Interpreter/IncrementalExecutor.h | 1 - clang/lib/Interpreter/Interpreter.cpp | 1 - clang/lib/Interpreter/ValuePrinter.cpp | 17 ++++++++++------- 6 files changed, 22 insertions(+), 13 deletions(-) diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 28f8d67811f0a2..9c4638cd6cccab 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -1071,6 +1071,8 @@ class ASTContext : public RefCountedBase<ASTContext> { Module *getCurrentNamedModule() const { return CurrentCXXNamedModule; } 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/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h index 43331b0fe92c96..0df53c13bddbdb 100644 --- a/clang/include/clang/Interpreter/Interpreter.h +++ b/clang/include/clang/Interpreter/Interpreter.h @@ -140,6 +140,14 @@ class Interpreter { public: virtual ~Interpreter(); + class SynthesizingCodeRAII { + + }; + + SynthesizingCodeRAII EnterCodeSynthesisScope() { + + } + static llvm::Expected<std::unique_ptr<Interpreter>> create(std::unique_ptr<CompilerInstance> CI); static llvm::Expected<std::unique_ptr<Interpreter>> @@ -187,6 +195,8 @@ class Interpreter { std::unique_ptr<llvm::Module> GenModule(); + + private: size_t getEffectivePTUSize() const; void markUserCodeStart(); diff --git a/clang/lib/Interpreter/IncrementalExecutor.cpp b/clang/lib/Interpreter/IncrementalExecutor.cpp index 54f5d798384cb1..490ad21fdde722 100644 --- a/clang/lib/Interpreter/IncrementalExecutor.cpp +++ b/clang/lib/Interpreter/IncrementalExecutor.cpp @@ -68,10 +68,6 @@ IncrementalExecutor::IncrementalExecutor(llvm::orc::ThreadSafeContext &TSC, IncrementalExecutor::~IncrementalExecutor() {} -llvm::Error IncrementalExecutor::addModule(std::unique_ptr<llvm::Module> &M) { - return Jit->addIRModule({std::move(M), TSCtx}); -} - llvm::Error IncrementalExecutor::addModule(PartialTranslationUnit &PTU) { llvm::orc::ResourceTrackerSP RT = Jit->getMainJITDylib().createResourceTracker(); diff --git a/clang/lib/Interpreter/IncrementalExecutor.h b/clang/lib/Interpreter/IncrementalExecutor.h index b6e7ddae401869..c2073a72156c10 100644 --- a/clang/lib/Interpreter/IncrementalExecutor.h +++ b/clang/lib/Interpreter/IncrementalExecutor.h @@ -52,7 +52,6 @@ class IncrementalExecutor { llvm::orc::LLJITBuilder &JITBuilder, llvm::Error &Err); ~IncrementalExecutor(); - llvm::Error addModule(std::unique_ptr<llvm::Module> &M); llvm::Error addModule(PartialTranslationUnit &PTU); llvm::Error removeModule(PartialTranslationUnit &PTU); llvm::Error runCtors() const; diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp index eb8b6592750467..1469cf757add5a 100644 --- a/clang/lib/Interpreter/Interpreter.cpp +++ b/clang/lib/Interpreter/Interpreter.cpp @@ -410,7 +410,6 @@ llvm::Error Interpreter::CreateExecutor() { void Interpreter::ResetExecutor() { IncrExecutor.reset(); } - llvm::Error Interpreter::ExecuteModule(std::unique_ptr<llvm::Module> &M) { if (!IncrExecutor) { auto Err = CreateExecutor(); diff --git a/clang/lib/Interpreter/ValuePrinter.cpp b/clang/lib/Interpreter/ValuePrinter.cpp index 565b170b04e29d..3f8c9a6f26cbbf 100644 --- a/clang/lib/Interpreter/ValuePrinter.cpp +++ b/clang/lib/Interpreter/ValuePrinter.cpp @@ -115,11 +115,9 @@ static std::string PrintEnum(const Value &V) { IsFirst = false; } } - - llvm::SmallString<24> ApStr; - AP.toString(ApStr, /*Radix=*/10); - - SS << " : " << PrintQualType(Ctx, ED->getIntegerType()) << " " << ApStr; + llvm::SmallString<64> APStr; + AP.toString(APStr, /*Radix=*/10); + SS << " : " << PrintQualType(Ctx, ED->getIntegerType()) << " " << APStr; return Str; } @@ -279,10 +277,15 @@ static llvm::Expected<llvm::orc::ExecutorAddr> CompileDecl(Interpreter &Interp, ASTConsumer &Consumer = Interp.getCompilerInstance()->getASTConsumer(); Consumer.HandleTopLevelDecl(DeclGroupRef(D)); Interp.getCompilerInstance()->getSema().PerformPendingInstantiations(); - Consumer.HandleTranslationUnit(Interp.getASTContext()); + 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<llvm::Module> M = Interp.GenModule()) { - if (llvm::Error Err = Interp.ExecuteModule(M)) + PartialTranslationUnit PTU = {TUPart, std::move(M)}; + if (llvm::Error Err = Interp.Execute(PTU)) return Err; ASTNameGenerator ASTNameGen(Interp.getASTContext()); llvm::Expected<llvm::orc::ExecutorAddr> AddrOrErr = _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits