diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h --- a/clang/include/clang/Interpreter/Interpreter.h +++ b/clang/include/clang/Interpreter/Interpreter.h @@ -35,6 +35,7 @@ namespace clang { class Value; +class Parser; class CompilerInstance; class IncrementalExecutor; class IncrementalParser; @@ -96,6 +97,8 @@ std::string CreateUniqName(std::string Base); + Parser &getParser() const; + std::list &getPTUs(); enum InterfaceKind { NoAlloc, WithAlloc, CopyArray }; diff --git a/clang/lib/Headers/CMakeLists.txt b/clang/lib/Headers/CMakeLists.txt --- a/clang/lib/Headers/CMakeLists.txt +++ b/clang/lib/Headers/CMakeLists.txt @@ -19,6 +19,7 @@ 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 --- /dev/null +++ b/clang/lib/Headers/__clang_interpreter_runtime_printvalue.h @@ -0,0 +1,277 @@ +//===--- RuntimePrintValue.h - Incremental Compiation and Execution---*- 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 + +#include +#include +#include + +// 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 + +// Below overloads are all defined in the library itself. +REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const void *Ptr); + +REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const void **Ptr); + +REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const bool *Val); + +REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const char *Val); + +REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const signed char *Val); + +REPL_EXTERNAL_VISIBILITY std::string +PrintValueRuntime(const unsigned char *Val); + +REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const short *Val); + +REPL_EXTERNAL_VISIBILITY std::string +PrintValueRuntime(const unsigned short *Val); + +REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const int *Val); + +REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const unsigned int *Val); + +REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const long *Val); + +REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const long long *Val); + +REPL_EXTERNAL_VISIBILITY std::string +PrintValueRuntime(const unsigned long *Val); + +REPL_EXTERNAL_VISIBILITY std::string +PrintValueRuntime(const unsigned long long *Val); + +REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const float *Val); + +REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const double *Val); + +REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const long double *Val); + +REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const char *const *Val); + +REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const char **Val); + +namespace valuePrinterInternal { +REPL_EXTERNAL_VISIBILITY +extern const char *const kEmptyCollection; +} // namespace valuePrinterInternal + +// Collections internal +namespace collectionPrinterInternal { + +// Forward declaration, so recursion of containers possible. +template +REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const T *V, + const void * = 0); + +template +inline std::string PrintValueRuntime( + const T &V, + typename std::enable_if::value>::type * = 0) { + return PrintValueRuntime(&V); +} + +template +inline std::string PrintValueRuntime(const std::pair *V, + const void *AsMap = 0) { + if (AsMap) + return PrintValueRuntime(&V->first) + " => " + + PrintValueRuntime(&V->second); + return "{" + PrintValueRuntime(&V->first) + " , " + + PrintValueRuntime(&V->second) + "}"; +} + +// For std::vector elements +inline std::string PrintValueRuntime(const bool &B, const void * = 0) { + return PrintValueRuntime(&B); +} + +struct TypeTest { + template + static constexpr const void *isMap(const T *M, + const typename T::mapped_type *V = 0) { + return M; + } + static constexpr const void *isMap(const void *M) { return nullptr; } +}; + +// vector, set, deque etc. +template +inline auto PrintValueRuntime_impl( + const CollectionType *obj, + typename std::enable_if< + std::is_reference::value>::type * = 0) + -> decltype(std::end(*obj), std::string()) { + auto iter = obj->begin(), iterEnd = obj->end(); + if (iter == iterEnd) + return valuePrinterInternal::kEmptyCollection; + + const void *M = TypeTest::isMap(obj); + + std::string str("{ "); + str += PrintValueRuntime(&(*iter), M); + while (++iter != iterEnd) { + str += ", "; + str += PrintValueRuntime(&(*iter), M); + } + return str + " }"; +} + +// As above, but without ability to take address of elements. +template +inline auto PrintValueRuntime_impl( + const CollectionType *obj, + typename std::enable_if< + !std::is_referencebegin()))>::value>::type * = 0) + -> decltype(++(obj->begin()), obj->end(), std::string()) { + auto iter = obj->begin(), iterEnd = obj->end(); + if (iter == iterEnd) + return valuePrinterInternal::kEmptyCollection; + + std::string str("{ "); + str += PrintValueRuntime(*iter); + while (++iter != iterEnd) { + str += ", "; + str += PrintValueRuntime(*iter); + } + return str + " }"; +} +} // namespace collectionPrinterInternal + +// Collections +template +inline auto PrintValueRuntime(const CollectionType *obj) + -> decltype(collectionPrinterInternal::PrintValueRuntime_impl(obj), + std::string()) { + return collectionPrinterInternal::PrintValueRuntime_impl(obj); +} + +// Arrays +template +inline std::string PrintValueRuntime(const T (*obj)[N]) { + if (N == 0) + return valuePrinterInternal::kEmptyCollection; + + std::string str = "{ "; + str += PrintValueRuntime(*obj + 0); + for (size_t i = 1; i < N; ++i) { + str += ", "; + str += PrintValueRuntime(*obj + i); + } + return str + " }"; +} + +// Tuples +template +inline std::string PrintValueRuntime(std::tuple *); + +namespace collectionPrinterInternal { +// We loop at compile time from element 0 to element TUPLE_SIZE - 1 +// of the tuple. The running index is N which has as initial value +// TUPLE_SIZE. We can therefore stop the iteration and account for the +// empty tuple case with one single specialisation. +template (), + std::size_t TUPLE_SIZE = std::tuple_size()> +struct tuplePrinter { + static std::string print(TUPLE *t) { + constexpr std::size_t elementNumber = TUPLE_SIZE - N; + std::string ret; + if (elementNumber) + ret += ", "; + ret += 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; + ret += tuplePrinter::print((TUPLE *)t); + return ret; + } +}; + +// Special case: no op if last element reached or empty tuple +template +struct tuplePrinter { + static std::string print(TUPLE *t) { return ""; } +}; + +template inline std::string tuplePairPrintValue(T *val) { + std::string ret("{ "); + ret += collectionPrinterInternal::tuplePrinter::print(val); + ret += " }"; + return ret; +} +} // namespace collectionPrinterInternal + +template +inline std::string PrintValueRuntime(std::tuple *val) { + using T = std::tuple; + if (std::tuple_size::value == 0) + return valuePrinterInternal::kEmptyCollection; + return collectionPrinterInternal::tuplePairPrintValue(val); +} + +template +inline std::string PrintValueRuntime(std::pair *val) { + using T = std::pair; + return collectionPrinterInternal::tuplePairPrintValue(val); +} + +namespace collectionPrinterInternal { +// Keep this last to allow picking up all specializations above. +template std::string PrintValueRuntime(const T *V, const void *) { + return PrintValueRuntime(V); +} +} // namespace collectionPrinterInternal + +// unique_ptr: +template +inline std::string PrintValueRuntime(std::unique_ptr *val) { + auto ptr = val->get(); + // PrintValueRuntime dereference its argument. use cast to 'const void**' to + // get the same printout as a regular pointer. + return "std::unique_ptr -> " + PrintValueRuntime((const void **)&ptr); +} + +// shared_ptr: +template +inline std::string PrintValueRuntime(std::shared_ptr *val) { + auto ptr = val->get(); + // PrintValueRuntime dereference its argument. use cast to 'const void**' to + // get the same printout as a regular pointer. + return "std::shared_ptr -> " + PrintValueRuntime((const void **)&ptr); +} + +// weak_ptr: +template inline std::string PrintValueRuntime(std::weak_ptr *val) { + auto ptr = val->lock().get(); + // PrintValueRuntime dereference its argument. use cast to 'const void**' to + // get the same printout as a regular pointer. + return "std::weak_ptr -> " + PrintValueRuntime((const void **)&ptr); +} +#endif diff --git a/clang/lib/Interpreter/CMakeLists.txt b/clang/lib/Interpreter/CMakeLists.txt --- a/clang/lib/Interpreter/CMakeLists.txt +++ b/clang/lib/Interpreter/CMakeLists.txt @@ -12,8 +12,9 @@ IncrementalExecutor.cpp IncrementalParser.cpp Interpreter.cpp - Value.cpp InterpreterUtils.cpp + Value.cpp + ValuePrinter.cpp DEPENDS intrinsics_gen diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp --- a/clang/lib/Interpreter/Interpreter.cpp +++ b/clang/lib/Interpreter/Interpreter.cpp @@ -238,6 +238,8 @@ return IncrParser->getCI(); } +Parser &Interpreter::getParser() const { return IncrParser->getParser(); } + llvm::Expected Interpreter::getExecutionEngine() { if (!IncrExecutor) { if (auto Err = CreateExecutor()) diff --git a/clang/lib/Interpreter/Value.cpp b/clang/lib/Interpreter/Value.cpp --- a/clang/lib/Interpreter/Value.cpp +++ b/clang/lib/Interpreter/Value.cpp @@ -201,13 +201,25 @@ void Value::dump() const { print(llvm::outs()); } -void Value::printType(llvm::raw_ostream &Out) const { - Out << "Not implement yet.\n"; -} -void Value::printData(llvm::raw_ostream &Out) const { - Out << "Not implement yet.\n"; -} +// Put them in the header! +std::string PrintType(const Value &V); +std::string PrintData(const Value &V); + +void Value::printType(llvm::raw_ostream &Out) const { Out << PrintType(*this); } + +void Value::printData(llvm::raw_ostream &Out) const { Out << PrintData(*this); } + void Value::print(llvm::raw_ostream &Out) const { assert(OpaqueType != nullptr && "Can't print default Value"); - Out << "Not implement yet.\n"; + // 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; } diff --git a/clang/lib/Interpreter/ValuePrinter.cpp b/clang/lib/Interpreter/ValuePrinter.cpp new file mode 100644 --- /dev/null +++ b/clang/lib/Interpreter/ValuePrinter.cpp @@ -0,0 +1,524 @@ +#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/Parse/Parser.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) { + 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 >' rather than 'a>'. + Policy.SplitTemplateClosers = true; + + class LocalPrintingPolicyRAII { + public: + LocalPrintingPolicyRAII(ASTContext &Ctx, PrintingPolicy &PPol) + : Context(Ctx), Policy(Ctx.getPrintingPolicy()) { + Context.setPrintingPolicy(PPol); + } + ~LocalPrintingPolicyRAII() { Context.setPrintingPolicy(Policy); } + + private: + ASTContext &Context; + PrintingPolicy Policy; + } X(Ctx, Policy); + + const QualType NonRefTy = QT.getNonReferenceType(); + + if (const auto *TTy = llvm::dyn_cast(NonRefTy)) + SS << PrintDeclType(NonRefTy, TTy->getDecl()); + else if (const auto *TRy = dyn_cast(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(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)) + SS << GetFullTypeName(Ctx, Canon); + else if (llvm::isa(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 = 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; + } + } + + SS << " : " << PrintQualType(Ctx, ED->getIntegerType()) << " " + << llvm::toString(AP, /*Radix=*/10); + 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; +} + +// TODO: Encodings. +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. +// FIXME: It doesn't work. +static std::string PrintString(const char *const *Ptr) { + std::string Str; + llvm::raw_string_ostream SS(Str); + + const char *Start = *Ptr; + if (!Start) + return "nullptr"; + + // const char *End = Start + N; + // bool IsValid = utils::isAddressValid(Start); + // if (IsValid) { + // // 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 (!(IsValid = utils::isAddressValid(End)) && N > 1024) { + // N -= PAGE_SIZE; + // End = Start + N; + // } + // } + // if (!IsValid) { + // SS << "Invalid"; + // } + + // 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::Parser::ParseScope PS( + &Interp.getParser(), clang::Scope::FnScope | clang::Scope::BlockScope); + + 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()); + + // For `auto foo = bar;` decls, we are interested in the deduced type, i.e. + // AutoType 0x55e5ac848030 'int *' sugar + // `-PointerType 0x55e5ac847f70 'int *' << this type + // `-BuiltinType 0x55e5ab517420 'int' + if (const auto *AT = llvm::dyn_cast(QT.getTypePtr())) { + if (AT->isDeduced()) + QT = AT->getDeducedType(); + } + + 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 std::string SynthesizeRuntimePrint(const Value &V) { + Interpreter &Interp = V.getInterpreter(); + Sema &S = Interp.getCompilerInstance()->getSema(); + ASTContext &Ctx = S.getASTContext(); + + // Only include once when the first time we need it. Technically speaking it's + // fine to include it several times since we have the header guard, but we + // still need to parse it, which is relatively expensive. + static bool Included = false; + if (!Included) { + llvm::cantFail(Interp.ParseAndExecute( + "#include <__clang_interpreter_runtime_printvalue.h>")); + Included = true; + } + // 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(StdStringDecl); + assert(StdStringTyDecl && "Cannot find type of std::string"); + + // Create the wrapper function. + DeclarationName DeclName = + &Ctx.Idents.get(Interp.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); + + const void *ValPtr = V.getPtr(); + if (!V.isManuallyAlloc()) + ValPtr = &ValPtr; + + BuildWrapperBody(Interp, S, Ctx, WrapperFD, V.getType(), ValPtr); + + auto AddrOrErr = Interp.CompileDecl(WrapperFD); + if (!AddrOrErr) + llvm::logAllUnhandledErrors(AddrOrErr.takeError(), llvm::errs(), + "Fail to get symbol address"); + if (auto *Main = + llvm::jitTargetAddressToPointer(AddrOrErr.get())) + 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); + return "Not implemented yet"; +} + +REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const char **Val) { + // return PrintString(Val); + return PrintString(Val); +} + +REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const std::string *Val) { + // return PrintString(Val); + return "Not implemented yet"; +} + +std::string PrintData(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()) { + llvm_unreachable("Not implemented yet"); + } else if ((NonRefTy->isPointerType() || NonRefTy->isMemberPointerType()) && + NonRefTy->getPointeeType()->isFunctionProtoType()) { + // Function pointer. + llvm_unreachable("Not implemented yet"); + } else if (auto *BT = DesugaredTy.getCanonicalType()->getAs()) { + switch (BT->getKind()) { + case BuiltinType::Bool: { + bool Data = V.getBool(); + return PrintValueRuntime(&Data); + break; + } + case BuiltinType::Char_S: + case BuiltinType::SChar: { + auto Data = V.getSChar(); + SS << PrintValueRuntime(&Data); + break; + } + case BuiltinType::Short: { + auto Data = V.getShort(); + SS << PrintValueRuntime(&Data); + break; + } + case BuiltinType::Int: { + auto Data = V.getInt(); + SS << PrintValueRuntime(&Data); + break; + } + case BuiltinType::Long: { + auto Data = V.getLong(); + SS << PrintValueRuntime(&Data); + break; + } + case BuiltinType::LongLong: { + auto Data = V.getLongLong(); + SS << PrintValueRuntime(&Data); + break; + } + case BuiltinType::Char_U: + case BuiltinType::UChar: { + auto Data = V.getUChar(); + SS << PrintValueRuntime(&Data); + break; + } + case BuiltinType::UShort: { + auto Data = V.getUShort(); + SS << PrintValueRuntime(&Data); + break; + } + case BuiltinType::UInt: { + auto Data = V.getUInt(); + SS << PrintValueRuntime(&Data); + break; + } + case BuiltinType::ULong: { + auto Data = V.getUInt(); + SS << PrintValueRuntime(&Data); + break; + } + case BuiltinType::ULongLong: { + auto Data = V.getULongLong(); + SS << PrintValueRuntime(&Data); + break; + } + case BuiltinType::Float: { + auto Data = V.getFloat(); + SS << PrintValueRuntime(&Data); + break; + } + case BuiltinType::Double: { + auto Data = V.getDouble(); + SS << PrintValueRuntime(&Data); + break; + } + case BuiltinType::LongDouble: { + auto Data = V.getLongDouble(); + SS << PrintValueRuntime(&Data); + break; + } + default: + llvm_unreachable("Unknown Builtintype kind"); + } + } else if (auto *CXXRD = NonRefTy->getAsCXXRecordDecl(); + CXXRD && CXXRD->isLambda()) { + return PrintAddress(V.getPtr(), '@'); + } else { + // All fails then generate a runtime call, this is slow. + SS << SynthesizeRuntimePrint(V); + } + return Str; +} + +std::string PrintType(const Value &V) { + ASTContext &Ctx = V.getASTContext(); + QualType QT = V.getType(); + + return PrintQualType(Ctx, QT); +} diff --git a/clang/test/Interpreter/pretty-print.cpp b/clang/test/Interpreter/pretty-print.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Interpreter/pretty-print.cpp @@ -0,0 +1,44 @@ +// 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 | FileCheck %s +char c = 'a'; +c +// CHECK: (char) 'a' + +int x = 42; +x +// CHECK-NEXT: (int) 42 + +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 S{}; +S s; +s +// CHECK-NEXT: (S &) [[Addr:@0x.*]] + +S{} +// CHECK-NEXT: (S) [[Addr:@0x.*]] + +struct SS { ~SS() {} }; +SS{} +// CHECK-NEXT: (SS) [[Addr:@0x.*]] + +#include + +auto p1 = std::make_shared(42); +p1 +// CHECK-NEXT: (shared_ptr &) std::shared_ptr -> [[Addr:@0x.*]] + +%quit +