junaire updated this revision to Diff 509306.
junaire added a comment.
Update + Rebase
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D146809/new/
https://reviews.llvm.org/D146809
Files:
clang/lib/Headers/CMakeLists.txt
clang/lib/Headers/__clang_interpreter_runtime_printvalue.h
clang/lib/Interpreter/CMakeLists.txt
clang/lib/Interpreter/Value.cpp
clang/lib/Interpreter/ValuePrinter.cpp
clang/test/Interpreter/pretty-print.cpp
Index: clang/test/Interpreter/pretty-print.cpp
===================================================================
--- /dev/null
+++ clang/test/Interpreter/pretty-print.cpp
@@ -0,0 +1,38 @@
+// 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.*]]
+
+%quit
+
Index: clang/lib/Interpreter/ValuePrinter.cpp
===================================================================
--- /dev/null
+++ clang/lib/Interpreter/ValuePrinter.cpp
@@ -0,0 +1,524 @@
+#include "ASTHelpers.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 <cassert>
+#include <string>
+
+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<b<c> >' rather than 'a<b<c>>'.
+ 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<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 = 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;
+ }
+ }
+
+ 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<AutoType>(QT.getTypePtr())) {
+ if (AT->isDeduced())
+ QT = AT->getDeducedType();
+ }
+
+ 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 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<TypeDecl>(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<std::string (*)()>(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<BuiltinType>()) {
+ 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);
+}
Index: clang/lib/Interpreter/Value.cpp
===================================================================
--- clang/lib/Interpreter/Value.cpp
+++ clang/lib/Interpreter/Value.cpp
@@ -177,13 +177,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;
}
Index: clang/lib/Interpreter/CMakeLists.txt
===================================================================
--- clang/lib/Interpreter/CMakeLists.txt
+++ clang/lib/Interpreter/CMakeLists.txt
@@ -13,6 +13,7 @@
IncrementalParser.cpp
Interpreter.cpp
Value.cpp
+ ValuePrinter.cpp
ASTHelpers.cpp
DEPENDS
Index: clang/lib/Headers/__clang_interpreter_runtime_printvalue.h
===================================================================
--- /dev/null
+++ 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 <memory>
+#include <string>
+#include <type_traits>
+
+// 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 <typename T>
+REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const T *V,
+ const void * = 0);
+
+template <typename T>
+inline std::string PrintValueRuntime(
+ const T &V,
+ typename std::enable_if<std::is_pointer<decltype(&V)>::value>::type * = 0) {
+ return PrintValueRuntime(&V);
+}
+
+template <typename T0, typename T1>
+inline std::string PrintValueRuntime(const std::pair<T1, T0> *V,
+ const void *AsMap = 0) {
+ if (AsMap)
+ return PrintValueRuntime(&V->first) + " => " +
+ PrintValueRuntime(&V->second);
+ return "{" + PrintValueRuntime(&V->first) + " , " +
+ PrintValueRuntime(&V->second) + "}";
+}
+
+// For std::vector<bool> elements
+inline std::string PrintValueRuntime(const bool &B, const void * = 0) {
+ return PrintValueRuntime(&B);
+}
+
+struct TypeTest {
+ template <class T>
+ 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 <typename CollectionType>
+inline auto PrintValueRuntime_impl(
+ const CollectionType *obj,
+ typename std::enable_if<
+ std::is_reference<decltype(*std::begin(*obj))>::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 <typename CollectionType>
+inline auto PrintValueRuntime_impl(
+ const CollectionType *obj,
+ typename std::enable_if<
+ !std::is_reference<decltype(*(obj->begin()))>::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 <typename CollectionType>
+inline auto PrintValueRuntime(const CollectionType *obj)
+ -> decltype(collectionPrinterInternal::PrintValueRuntime_impl(obj),
+ std::string()) {
+ return collectionPrinterInternal::PrintValueRuntime_impl(obj);
+}
+
+// Arrays
+template <typename T, size_t N>
+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 <class... ARGS>
+inline std::string PrintValueRuntime(std::tuple<ARGS...> *);
+
+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 <class TUPLE, std::size_t N = std::tuple_size<TUPLE>(),
+ std::size_t TUPLE_SIZE = std::tuple_size<TUPLE>()>
+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<elementNumber>(*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<TUPLE, Nm1>::print((TUPLE *)t);
+ return ret;
+ }
+};
+
+// Special case: no op if last element reached or empty tuple
+template <class TUPLE, std::size_t TUPLE_SIZE>
+struct tuplePrinter<TUPLE, 0, TUPLE_SIZE> {
+ static std::string print(TUPLE *t) { return ""; }
+};
+
+template <class T> inline std::string tuplePairPrintValue(T *val) {
+ std::string ret("{ ");
+ ret += collectionPrinterInternal::tuplePrinter<T>::print(val);
+ ret += " }";
+ return ret;
+}
+} // namespace collectionPrinterInternal
+
+template <class... ARGS>
+inline std::string PrintValueRuntime(std::tuple<ARGS...> *val) {
+ using T = std::tuple<ARGS...>;
+ if (std::tuple_size<T>::value == 0)
+ return valuePrinterInternal::kEmptyCollection;
+ return collectionPrinterInternal::tuplePairPrintValue<T>(val);
+}
+
+template <class... ARGS>
+inline std::string PrintValueRuntime(std::pair<ARGS...> *val) {
+ using T = std::pair<ARGS...>;
+ return collectionPrinterInternal::tuplePairPrintValue<T>(val);
+}
+
+namespace collectionPrinterInternal {
+// Keep this last to allow picking up all specializations above.
+template <typename T> std::string PrintValueRuntime(const T *V, const void *) {
+ return PrintValueRuntime(V);
+}
+} // namespace collectionPrinterInternal
+
+// unique_ptr<T>:
+template <class T>
+inline std::string PrintValueRuntime(std::unique_ptr<T> *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<T>:
+template <class T>
+inline std::string PrintValueRuntime(std::shared_ptr<T> *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<T>:
+template <class T> inline std::string PrintValueRuntime(std::weak_ptr<T> *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
Index: clang/lib/Headers/CMakeLists.txt
===================================================================
--- clang/lib/Headers/CMakeLists.txt
+++ clang/lib/Headers/CMakeLists.txt
@@ -19,6 +19,7 @@
tgmath.h
unwind.h
varargs.h
+ __clang_interpreter_runtime_printvalue.h
)
set(arm_common_files
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits