[clang] [clang-repl] Implement value printing of custom types (PR #84769)

Vassil Vassilev via cfe-commits cfe-commits at lists.llvm.org
Fri Sep 6 02:00:52 PDT 2024


https://github.com/vgvassilev updated https://github.com/llvm/llvm-project/pull/84769

>From 0f6d68c2c1b2930e74438da67a29307c2e77b383 Mon Sep 17 00:00:00 2001
From: Vassil Vassilev <v.g.vassilev at gmail.com>
Date: Sun, 16 Jul 2023 21:18:26 +0000
Subject: [PATCH 1/3] [clang-repl] Implement value printing of custom types.

The work started as part of https://reviews.llvm.org/D146809

This patch reworks a lot of the infrastructure to enable printing of types for
out-of-process execution on embedded devices.
---
 clang/include/clang/AST/ASTContext.h          |   2 +
 .../clang/Frontend/MultiplexConsumer.h        |   3 +-
 clang/include/clang/Interpreter/Interpreter.h |  83 +-
 clang/include/clang/Interpreter/Value.h       |  11 +-
 clang/lib/Frontend/MultiplexConsumer.cpp      |   7 +
 clang/lib/Headers/CMakeLists.txt              |   1 +
 .../__clang_interpreter_runtime_printvalue.h  | 269 ++++++
 clang/lib/Interpreter/CMakeLists.txt          |   1 +
 clang/lib/Interpreter/DeviceOffload.cpp       |  10 +-
 clang/lib/Interpreter/DeviceOffload.h         |  14 +-
 clang/lib/Interpreter/IncrementalExecutor.cpp |   2 +-
 clang/lib/Interpreter/IncrementalExecutor.h   |   4 +-
 clang/lib/Interpreter/IncrementalParser.cpp   | 253 +----
 clang/lib/Interpreter/IncrementalParser.h     |  45 +-
 clang/lib/Interpreter/Interpreter.cpp         | 646 +++++--------
 clang/lib/Interpreter/InterpreterUtils.cpp    | 395 +++++++-
 clang/lib/Interpreter/InterpreterUtils.h      |  18 +
 .../Interpreter/InterpreterValuePrinter.cpp   | 890 ++++++++++++++++++
 clang/lib/Interpreter/Value.cpp               |  38 +-
 clang/lib/Parse/ParseStmt.cpp                 |   3 +-
 clang/test/Interpreter/pretty-print.c         |  68 +-
 clang/test/Interpreter/pretty-print.cpp       | 170 ++++
 clang/tools/clang-repl/CMakeLists.txt         |  16 +
 clang/tools/clang-repl/ClangRepl.cpp          |   1 -
 .../Interpreter/CodeCompletionTest.cpp        |   2 +-
 .../Interpreter/InterpreterExtensionsTest.cpp |  68 +-
 26 files changed, 2197 insertions(+), 823 deletions(-)
 create mode 100644 clang/lib/Headers/__clang_interpreter_runtime_printvalue.h
 create mode 100644 clang/lib/Interpreter/InterpreterValuePrinter.cpp
 create mode 100644 clang/test/Interpreter/pretty-print.cpp

diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 58a820508da42b..c9840820a37ee9 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -1099,6 +1099,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
   bool isInSameModule(const Module *M1, const Module *M2);
 
   TranslationUnitDecl *getTranslationUnitDecl() const {
+    assert(TUDecl->getMostRecentDecl() == TUDecl &&
+           "The active TU is not current one!");
     return TUDecl->getMostRecentDecl();
   }
   void addTranslationUnitDecl() {
diff --git a/clang/include/clang/Frontend/MultiplexConsumer.h b/clang/include/clang/Frontend/MultiplexConsumer.h
index 3a7670d7a51aa6..b190750bb29fb8 100644
--- a/clang/include/clang/Frontend/MultiplexConsumer.h
+++ b/clang/include/clang/Frontend/MultiplexConsumer.h
@@ -53,6 +53,7 @@ class MultiplexConsumer : public SemaConsumer {
 public:
   // Takes ownership of the pointers in C.
   MultiplexConsumer(std::vector<std::unique_ptr<ASTConsumer>> C);
+  MultiplexConsumer(std::unique_ptr<ASTConsumer> C);
   ~MultiplexConsumer() override;
 
   // ASTConsumer
@@ -80,7 +81,7 @@ class MultiplexConsumer : public SemaConsumer {
   void InitializeSema(Sema &S) override;
   void ForgetSema() override;
 
-private:
+protected:
   std::vector<std::unique_ptr<ASTConsumer>> Consumers; // Owns these.
   std::unique_ptr<MultiplexASTMutationListener> MutationListener;
   std::unique_ptr<MultiplexASTDeserializationListener> DeserializationListener;
diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h
index 1234608bb58647..195545a5b393b0 100644
--- a/clang/include/clang/Interpreter/Interpreter.h
+++ b/clang/include/clang/Interpreter/Interpreter.h
@@ -14,11 +14,9 @@
 #ifndef LLVM_CLANG_INTERPRETER_INTERPRETER_H
 #define LLVM_CLANG_INTERPRETER_INTERPRETER_H
 
-#include "clang/AST/Decl.h"
 #include "clang/AST/GlobalDecl.h"
 #include "clang/Interpreter/PartialTranslationUnit.h"
 #include "clang/Interpreter/Value.h"
-#include "clang/Sema/Ownership.h"
 
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ExecutionEngine/JITSymbol.h"
@@ -38,6 +36,9 @@ class ThreadSafeContext;
 namespace clang {
 
 class CompilerInstance;
+class CodeGenerator;
+class CXXRecordDecl;
+class Decl;
 class IncrementalExecutor;
 class IncrementalParser;
 
@@ -77,26 +78,29 @@ class IncrementalCompilerBuilder {
   llvm::StringRef CudaSDKPath;
 };
 
-/// Generate glue code between the Interpreter's built-in runtime and user code.
-class RuntimeInterfaceBuilder {
-public:
-  virtual ~RuntimeInterfaceBuilder() = default;
-
-  using TransformExprFunction = ExprResult(RuntimeInterfaceBuilder *Builder,
-                                           Expr *, ArrayRef<Expr *>);
-  virtual TransformExprFunction *getPrintValueTransformer() = 0;
-};
+class IncrementalAction;
+class InProcessPrintingASTConsumer;
 
 /// Provides top-level interfaces for incremental compilation and execution.
 class Interpreter {
+  friend class Value;
+  friend InProcessPrintingASTConsumer;
+
   std::unique_ptr<llvm::orc::ThreadSafeContext> TSCtx;
+  /// Long-lived, incremental parsing action.
+  std::unique_ptr<IncrementalAction> Act;
   std::unique_ptr<IncrementalParser> IncrParser;
   std::unique_ptr<IncrementalExecutor> IncrExecutor;
-  std::unique_ptr<RuntimeInterfaceBuilder> RuntimeIB;
 
   // An optional parser for CUDA offloading
   std::unique_ptr<IncrementalParser> DeviceParser;
 
+  std::unique_ptr<llvm::orc::LLJITBuilder> JITBuilder;
+
+  /// List containing every information about every incrementally parsed piece
+  /// of code.
+  std::list<PartialTranslationUnit> PTUs;
+
   unsigned InitPTUSize = 0;
 
   // This member holds the last result of the value printing. It's a class
@@ -104,15 +108,26 @@ class Interpreter {
   // printing happens, it's in an invalid state.
   Value LastValue;
 
-  // Add a call to an Expr to report its result. We query the function from
-  // RuntimeInterfaceBuilder once and store it as a function pointer to avoid
-  // frequent virtual function calls.
-  RuntimeInterfaceBuilder::TransformExprFunction *AddPrintValueCall = nullptr;
+  // The cached declaration of std::string used as a return type for the built
+  // trampoline. This is done in C++ to simplify the memory management for
+  // user-defined printing functions.
+  Decl *StdString = nullptr;
+
+  // A cache for the compiled destructors used to for de-allocation of managed
+  // clang::Values.
+  llvm::DenseMap<CXXRecordDecl *, llvm::orc::ExecutorAddr> Dtors;
+
+  std::array<Expr *, 4> ValuePrintingInfo = {0};
+
+  /// When CodeGen is created the first llvm::Module gets cached in many places
+  /// and we must keep it alive.
+  std::unique_ptr<llvm::Module> CachedInCodeGenModule;
 
 protected:
   // Derived classes can use an extended interface of the Interpreter.
   Interpreter(std::unique_ptr<CompilerInstance> CI, llvm::Error &Err,
-              std::unique_ptr<llvm::orc::LLJITBuilder> JITBuilder = nullptr);
+              std::unique_ptr<llvm::orc::LLJITBuilder> JITBuilder = nullptr,
+              std::unique_ptr<clang::ASTConsumer> Consumer = nullptr);
 
   // Create the internal IncrementalExecutor, or re-create it after calling
   // ResetExecutor().
@@ -122,15 +137,8 @@ class Interpreter {
   // JIT engine. In particular, it doesn't run cleanup or destructors.
   void ResetExecutor();
 
-  // Lazily construct the RuntimeInterfaceBuilder. The provided instance will be
-  // used for the entire lifetime of the interpreter. The default implementation
-  // targets the in-process __clang_Interpreter runtime. Override this to use a
-  // custom runtime.
-  virtual std::unique_ptr<RuntimeInterfaceBuilder> FindRuntimeInterface();
-
 public:
   virtual ~Interpreter();
-
   static llvm::Expected<std::unique_ptr<Interpreter>>
   create(std::unique_ptr<CompilerInstance> CI);
   static llvm::Expected<std::unique_ptr<Interpreter>>
@@ -145,7 +153,6 @@ class Interpreter {
   llvm::Expected<PartialTranslationUnit &> Parse(llvm::StringRef Code);
   llvm::Error Execute(PartialTranslationUnit &T);
   llvm::Error ParseAndExecute(llvm::StringRef Code, Value *V = nullptr);
-  llvm::Expected<llvm::orc::ExecutorAddr> CompileDtorCall(CXXRecordDecl *CXXRD);
 
   /// Undo N previous incremental inputs.
   llvm::Error Undo(unsigned N = 1);
@@ -167,23 +174,29 @@ class Interpreter {
   llvm::Expected<llvm::orc::ExecutorAddr>
   getSymbolAddressFromLinkerName(llvm::StringRef LinkerName) const;
 
-  enum InterfaceKind { NoAlloc, WithAlloc, CopyArray, NewTag };
-
-  const llvm::SmallVectorImpl<Expr *> &getValuePrintingInfo() const {
-    return ValuePrintingInfo;
-  }
-
-  Expr *SynthesizeExpr(Expr *E);
+  std::unique_ptr<llvm::Module> GenModule();
 
 private:
   size_t getEffectivePTUSize() const;
   void markUserCodeStart();
 
-  llvm::DenseMap<CXXRecordDecl *, llvm::orc::ExecutorAddr> Dtors;
+  /// @}
+  /// @name Value and pretty printing support
+  /// @{
 
-  llvm::SmallVector<Expr *, 4> ValuePrintingInfo;
+  std::string ValueDataToString(const Value &V);
+  std::string ValueTypeToString(const Value &V) const;
 
-  std::unique_ptr<llvm::orc::LLJITBuilder> JITBuilder;
+  llvm::Expected<Expr *> AttachValuePrinting(Expr *E);
+
+  // When we deallocate clang::Value we need to run the destructor of the type.
+  // This function forces emission of the needed dtor.
+  llvm::Expected<llvm::orc::ExecutorAddr> CompileDtorCall(CXXRecordDecl *CXXRD);
+
+  /// @}
+  /// @name Code generation
+  /// @{
+  CodeGenerator *getCodeGen() const;
 };
 } // namespace clang
 
diff --git a/clang/include/clang/Interpreter/Value.h b/clang/include/clang/Interpreter/Value.h
index d70e8f8719026b..5417c550e7f985 100644
--- a/clang/include/clang/Interpreter/Value.h
+++ b/clang/include/clang/Interpreter/Value.h
@@ -49,9 +49,10 @@ class raw_ostream;
 namespace clang {
 
 class ASTContext;
-class Interpreter;
 class QualType;
 
+class Interpreter;
+
 #if defined(_WIN32)
 // REPL_EXTERNAL_VISIBILITY are symbols that we need to be able to locate
 // at runtime. On Windows, this requires them to be exported from any of the
@@ -118,9 +119,9 @@ class REPL_EXTERNAL_VISIBILITY Value {
   ~Value();
 
   void printType(llvm::raw_ostream &Out) const;
-  void printData(llvm::raw_ostream &Out) const;
-  void print(llvm::raw_ostream &Out) const;
-  void dump() const;
+  void printData(llvm::raw_ostream &Out);
+  void print(llvm::raw_ostream &Out);
+  void dump();
   void clear();
 
   ASTContext &getASTContext();
@@ -138,6 +139,7 @@ class REPL_EXTERNAL_VISIBILITY Value {
   void setOpaqueType(void *Ty) { OpaqueType = Ty; }
 
   void *getPtr() const;
+  void **getPtrAddress() const;
   void setPtr(void *Ptr) { Data.m_Ptr = Ptr; }
 
 #define X(type, name)                                                          \
@@ -204,6 +206,5 @@ template <> inline void *Value::as() const {
     return Data.m_Ptr;
   return (void *)as<uintptr_t>();
 }
-
 } // namespace clang
 #endif
diff --git a/clang/lib/Frontend/MultiplexConsumer.cpp b/clang/lib/Frontend/MultiplexConsumer.cpp
index 2158d176d18929..3fd3c9bd69037a 100644
--- a/clang/lib/Frontend/MultiplexConsumer.cpp
+++ b/clang/lib/Frontend/MultiplexConsumer.cpp
@@ -298,6 +298,13 @@ MultiplexConsumer::MultiplexConsumer(
   }
 }
 
+MultiplexConsumer::MultiplexConsumer(std::unique_ptr<ASTConsumer> C)
+    : MultiplexConsumer([](std::unique_ptr<ASTConsumer> Consumer) {
+        std::vector<std::unique_ptr<ASTConsumer>> Consumers;
+        Consumers.push_back(std::move(Consumer));
+        return Consumers;
+      }(std::move(C))) {}
+
 MultiplexConsumer::~MultiplexConsumer() {}
 
 void MultiplexConsumer::Initialize(ASTContext &Context) {
diff --git a/clang/lib/Headers/CMakeLists.txt b/clang/lib/Headers/CMakeLists.txt
index 5a62538792f301..82d07fc77f143b 100644
--- a/clang/lib/Headers/CMakeLists.txt
+++ b/clang/lib/Headers/CMakeLists.txt
@@ -36,6 +36,7 @@ set(core_files
   tgmath.h
   unwind.h
   varargs.h
+  __clang_interpreter_runtime_printvalue.h
   )
 
 set(arm_common_files
diff --git a/clang/lib/Headers/__clang_interpreter_runtime_printvalue.h b/clang/lib/Headers/__clang_interpreter_runtime_printvalue.h
new file mode 100644
index 00000000000000..0a1367f970d0fb
--- /dev/null
+++ b/clang/lib/Headers/__clang_interpreter_runtime_printvalue.h
@@ -0,0 +1,269 @@
+//===--- __clang_interpreter_runtime_printvalue.h ---*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines runtime functions used to print STL components in
+// clang-repl. They are very heavy so we should only include it once and on
+// demand.
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_CLANG_INTERPRETER_RUNTIME_PRINT_VALUE_H
+#define LLVM_CLANG_INTERPRETER_RUNTIME_PRINT_VALUE_H
+
+#if !defined(__CLANG_REPL__)
+#error "This file should only be included by clang-repl!"
+#endif
+
+namespace caas {
+namespace runtime {}
+} // namespace caas
+using namespace caas::runtime;
+
+#include <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}";
+}
+
+// Forward declare the pre-compiled printing functions.
+#ifndef __DECL_PRINT_VALUE_RUNTIME
+#define __DECL_PRINT_VALUE_RUNTIME(type)                                       \
+  __REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const type *__Ptr)
+__DECL_PRINT_VALUE_RUNTIME(void);        //
+__DECL_PRINT_VALUE_RUNTIME(void *);      //
+__DECL_PRINT_VALUE_RUNTIME(char *const); //
+__DECL_PRINT_VALUE_RUNTIME(char *);      //
+__DECL_PRINT_VALUE_RUNTIME(bool);
+__DECL_PRINT_VALUE_RUNTIME(char);
+__DECL_PRINT_VALUE_RUNTIME(signed char);
+__DECL_PRINT_VALUE_RUNTIME(short);
+__DECL_PRINT_VALUE_RUNTIME(unsigned short);
+__DECL_PRINT_VALUE_RUNTIME(int);
+__DECL_PRINT_VALUE_RUNTIME(unsigned int);
+__DECL_PRINT_VALUE_RUNTIME(long);
+__DECL_PRINT_VALUE_RUNTIME(unsigned long);
+__DECL_PRINT_VALUE_RUNTIME(long long);
+__DECL_PRINT_VALUE_RUNTIME(unsigned long long);
+__DECL_PRINT_VALUE_RUNTIME(float);
+__DECL_PRINT_VALUE_RUNTIME(double);
+__DECL_PRINT_VALUE_RUNTIME(long double);
+#endif
+
+namespace __repl_runtime_detail {
+
+// Custom void_t implementation for C++11 compatibility
+template <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 6a069659ebb8db..8d95b78bd10bee 100644
--- a/clang/lib/Interpreter/CMakeLists.txt
+++ b/clang/lib/Interpreter/CMakeLists.txt
@@ -24,6 +24,7 @@ add_clang_library(clangInterpreter
   Interpreter.cpp
   InterpreterUtils.cpp
   Value.cpp
+  InterpreterValuePrinter.cpp
   ${WASM_SRC}
   PARTIAL_SOURCES_INTENDED
 
diff --git a/clang/lib/Interpreter/DeviceOffload.cpp b/clang/lib/Interpreter/DeviceOffload.cpp
index 07c9e3005e5fd3..4bf9ed28096893 100644
--- a/clang/lib/Interpreter/DeviceOffload.cpp
+++ b/clang/lib/Interpreter/DeviceOffload.cpp
@@ -15,6 +15,7 @@
 #include "clang/Basic/TargetOptions.h"
 #include "clang/CodeGen/ModuleBuilder.h"
 #include "clang/Frontend/CompilerInstance.h"
+#include "clang/Interpreter/PartialTranslationUnit.h"
 
 #include "llvm/IR/LegacyPassManager.h"
 #include "llvm/IR/Module.h"
@@ -24,11 +25,10 @@
 namespace clang {
 
 IncrementalCUDADeviceParser::IncrementalCUDADeviceParser(
-    Interpreter &Interp, std::unique_ptr<CompilerInstance> Instance,
-    IncrementalParser &HostParser, llvm::LLVMContext &LLVMCtx,
+    std::unique_ptr<CompilerInstance> Instance, IncrementalParser &HostParser,
     llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS,
-    llvm::Error &Err)
-    : IncrementalParser(Interp, std::move(Instance), LLVMCtx, Err),
+    llvm::Error &Err, const std::list<PartialTranslationUnit> &PTUs)
+    : IncrementalParser(std::move(Instance), Err), PTUs(PTUs),
       HostParser(HostParser), VFS(FS) {
   if (Err)
     return;
@@ -41,7 +41,7 @@ IncrementalCUDADeviceParser::IncrementalCUDADeviceParser(
   }
 }
 
-llvm::Expected<PartialTranslationUnit &>
+llvm::Expected<TranslationUnitDecl *>
 IncrementalCUDADeviceParser::Parse(llvm::StringRef Input) {
   auto PTU = IncrementalParser::Parse(Input);
   if (!PTU)
diff --git a/clang/lib/Interpreter/DeviceOffload.h b/clang/lib/Interpreter/DeviceOffload.h
index ce4f218c94c79d..b84870474841a5 100644
--- a/clang/lib/Interpreter/DeviceOffload.h
+++ b/clang/lib/Interpreter/DeviceOffload.h
@@ -18,19 +18,19 @@
 #include "llvm/Support/VirtualFileSystem.h"
 
 namespace clang {
-
+struct PartialTranslationUnit;
 class IncrementalCUDADeviceParser : public IncrementalParser {
+  const std::list<PartialTranslationUnit> &PTUs;
+
 public:
   IncrementalCUDADeviceParser(
-      Interpreter &Interp, std::unique_ptr<CompilerInstance> Instance,
-      IncrementalParser &HostParser, llvm::LLVMContext &LLVMCtx,
+      std::unique_ptr<CompilerInstance> Instance, IncrementalParser &HostParser,
       llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> VFS,
-      llvm::Error &Err);
+      llvm::Error &Err, const std::list<PartialTranslationUnit> &PTUs);
 
-  llvm::Expected<PartialTranslationUnit &>
-  Parse(llvm::StringRef Input) override;
+  llvm::Expected<TranslationUnitDecl *> Parse(llvm::StringRef Input) override;
 
-  // Generate PTX for the last PTU
+  // Generate PTX for the last PTU.
   llvm::Expected<llvm::StringRef> GeneratePTX();
 
   // Generate fatbinary contents in memory
diff --git a/clang/lib/Interpreter/IncrementalExecutor.cpp b/clang/lib/Interpreter/IncrementalExecutor.cpp
index 1824a5b4570a93..4d2adecaafce74 100644
--- a/clang/lib/Interpreter/IncrementalExecutor.cpp
+++ b/clang/lib/Interpreter/IncrementalExecutor.cpp
@@ -118,4 +118,4 @@ IncrementalExecutor::getSymbolAddress(llvm::StringRef Name,
   return SymOrErr->getAddress();
 }
 
-} // end namespace clang
+} // namespace clang
diff --git a/clang/lib/Interpreter/IncrementalExecutor.h b/clang/lib/Interpreter/IncrementalExecutor.h
index 7954cde36588bd..4e3afb7de26e00 100644
--- a/clang/lib/Interpreter/IncrementalExecutor.h
+++ b/clang/lib/Interpreter/IncrementalExecutor.h
@@ -32,9 +32,10 @@ class ThreadSafeContext;
 
 namespace clang {
 
-struct PartialTranslationUnit;
 class TargetInfo;
 
+struct PartialTranslationUnit;
+
 class IncrementalExecutor {
   using CtorDtorIterator = llvm::orc::CtorDtorIterator;
   std::unique_ptr<llvm::orc::LLJIT> Jit;
@@ -65,7 +66,6 @@ class IncrementalExecutor {
   static llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>>
   createDefaultJITBuilder(llvm::orc::JITTargetMachineBuilder JTMB);
 };
-
 } // end namespace clang
 
 #endif // LLVM_CLANG_LIB_INTERPRETER_INCREMENTALEXECUTOR_H
diff --git a/clang/lib/Interpreter/IncrementalParser.cpp b/clang/lib/Interpreter/IncrementalParser.cpp
index b7c809c45098ca..615f61e9aec1bc 100644
--- a/clang/lib/Interpreter/IncrementalParser.cpp
+++ b/clang/lib/Interpreter/IncrementalParser.cpp
@@ -13,233 +13,33 @@
 #include "IncrementalParser.h"
 
 #include "clang/AST/DeclContextInternals.h"
-#include "clang/CodeGen/BackendUtil.h"
-#include "clang/CodeGen/CodeGenAction.h"
-#include "clang/CodeGen/ModuleBuilder.h"
 #include "clang/Frontend/CompilerInstance.h"
-#include "clang/Frontend/FrontendAction.h"
-#include "clang/FrontendTool/Utils.h"
-#include "clang/Interpreter/Interpreter.h"
+#include "clang/Interpreter/PartialTranslationUnit.h"
 #include "clang/Parse/Parser.h"
 #include "clang/Sema/Sema.h"
-#include "llvm/Option/ArgList.h"
 #include "llvm/Support/CrashRecoveryContext.h"
 #include "llvm/Support/Error.h"
-#include "llvm/Support/Timer.h"
 
 #include <sstream>
 
 namespace clang {
 
-class IncrementalASTConsumer final : public ASTConsumer {
-  Interpreter &Interp;
-  std::unique_ptr<ASTConsumer> Consumer;
-
-public:
-  IncrementalASTConsumer(Interpreter &InterpRef, std::unique_ptr<ASTConsumer> C)
-      : Interp(InterpRef), Consumer(std::move(C)) {}
-
-  bool HandleTopLevelDecl(DeclGroupRef DGR) override final {
-    if (DGR.isNull())
-      return true;
-    if (!Consumer)
-      return true;
-
-    for (Decl *D : DGR)
-      if (auto *TSD = llvm::dyn_cast<TopLevelStmtDecl>(D);
-          TSD && TSD->isSemiMissing())
-        TSD->setStmt(Interp.SynthesizeExpr(cast<Expr>(TSD->getStmt())));
-
-    return Consumer->HandleTopLevelDecl(DGR);
-  }
-  void HandleTranslationUnit(ASTContext &Ctx) override final {
-    Consumer->HandleTranslationUnit(Ctx);
-  }
-  void HandleInlineFunctionDefinition(FunctionDecl *D) override final {
-    Consumer->HandleInlineFunctionDefinition(D);
-  }
-  void HandleInterestingDecl(DeclGroupRef D) override final {
-    Consumer->HandleInterestingDecl(D);
-  }
-  void HandleTagDeclDefinition(TagDecl *D) override final {
-    Consumer->HandleTagDeclDefinition(D);
-  }
-  void HandleTagDeclRequiredDefinition(const TagDecl *D) override final {
-    Consumer->HandleTagDeclRequiredDefinition(D);
-  }
-  void HandleCXXImplicitFunctionInstantiation(FunctionDecl *D) override final {
-    Consumer->HandleCXXImplicitFunctionInstantiation(D);
-  }
-  void HandleTopLevelDeclInObjCContainer(DeclGroupRef D) override final {
-    Consumer->HandleTopLevelDeclInObjCContainer(D);
-  }
-  void HandleImplicitImportDecl(ImportDecl *D) override final {
-    Consumer->HandleImplicitImportDecl(D);
-  }
-  void CompleteTentativeDefinition(VarDecl *D) override final {
-    Consumer->CompleteTentativeDefinition(D);
-  }
-  void CompleteExternalDeclaration(DeclaratorDecl *D) override final {
-    Consumer->CompleteExternalDeclaration(D);
-  }
-  void AssignInheritanceModel(CXXRecordDecl *RD) override final {
-    Consumer->AssignInheritanceModel(RD);
-  }
-  void HandleCXXStaticMemberVarInstantiation(VarDecl *D) override final {
-    Consumer->HandleCXXStaticMemberVarInstantiation(D);
-  }
-  void HandleVTable(CXXRecordDecl *RD) override final {
-    Consumer->HandleVTable(RD);
-  }
-  ASTMutationListener *GetASTMutationListener() override final {
-    return Consumer->GetASTMutationListener();
-  }
-  ASTDeserializationListener *GetASTDeserializationListener() override final {
-    return Consumer->GetASTDeserializationListener();
-  }
-  void PrintStats() override final { Consumer->PrintStats(); }
-  bool shouldSkipFunctionBody(Decl *D) override final {
-    return Consumer->shouldSkipFunctionBody(D);
-  }
-  static bool classof(const clang::ASTConsumer *) { return true; }
-};
-
-/// A custom action enabling the incremental processing functionality.
-///
-/// The usual \p FrontendAction expects one call to ExecuteAction and once it
-/// sees a call to \p EndSourceFile it deletes some of the important objects
-/// such as \p Preprocessor and \p Sema assuming no further input will come.
-///
-/// \p IncrementalAction ensures it keep its underlying action's objects alive
-/// as long as the \p IncrementalParser needs them.
-///
-class IncrementalAction : public WrapperFrontendAction {
-private:
-  bool IsTerminating = false;
-
-public:
-  IncrementalAction(CompilerInstance &CI, llvm::LLVMContext &LLVMCtx,
-                    llvm::Error &Err)
-      : WrapperFrontendAction([&]() {
-          llvm::ErrorAsOutParameter EAO(&Err);
-          std::unique_ptr<FrontendAction> Act;
-          switch (CI.getFrontendOpts().ProgramAction) {
-          default:
-            Err = llvm::createStringError(
-                std::errc::state_not_recoverable,
-                "Driver initialization failed. "
-                "Incremental mode for action %d is not supported",
-                CI.getFrontendOpts().ProgramAction);
-            return Act;
-          case frontend::ASTDump:
-            [[fallthrough]];
-          case frontend::ASTPrint:
-            [[fallthrough]];
-          case frontend::ParseSyntaxOnly:
-            Act = CreateFrontendAction(CI);
-            break;
-          case frontend::PluginAction:
-            [[fallthrough]];
-          case frontend::EmitAssembly:
-            [[fallthrough]];
-          case frontend::EmitBC:
-            [[fallthrough]];
-          case frontend::EmitObj:
-            [[fallthrough]];
-          case frontend::PrintPreprocessedInput:
-            [[fallthrough]];
-          case frontend::EmitLLVMOnly:
-            Act.reset(new EmitLLVMOnlyAction(&LLVMCtx));
-            break;
-          }
-          return Act;
-        }()) {}
-  FrontendAction *getWrapped() const { return WrappedAction.get(); }
-  TranslationUnitKind getTranslationUnitKind() override {
-    return TU_Incremental;
-  }
-
-  void ExecuteAction() override {
-    CompilerInstance &CI = getCompilerInstance();
-    assert(CI.hasPreprocessor() && "No PP!");
-
-    // Use a code completion consumer?
-    CodeCompleteConsumer *CompletionConsumer = nullptr;
-    if (CI.hasCodeCompletionConsumer())
-      CompletionConsumer = &CI.getCodeCompletionConsumer();
-
-    Preprocessor &PP = CI.getPreprocessor();
-    PP.EnterMainSourceFile();
-
-    if (!CI.hasSema())
-      CI.createSema(getTranslationUnitKind(), CompletionConsumer);
-  }
-
-  // Do not terminate after processing the input. This allows us to keep various
-  // clang objects alive and to incrementally grow the current TU.
-  void EndSourceFile() override {
-    // The WrappedAction can be nullptr if we issued an error in the ctor.
-    if (IsTerminating && getWrapped())
-      WrapperFrontendAction::EndSourceFile();
-  }
-
-  void FinalizeAction() {
-    assert(!IsTerminating && "Already finalized!");
-    IsTerminating = true;
-    EndSourceFile();
-  }
-};
-
-CodeGenerator *IncrementalParser::getCodeGen() const {
-  FrontendAction *WrappedAct = Act->getWrapped();
-  if (!WrappedAct->hasIRSupport())
-    return nullptr;
-  return static_cast<CodeGenAction *>(WrappedAct)->getCodeGenerator();
-}
-
 IncrementalParser::IncrementalParser() {}
 
-IncrementalParser::IncrementalParser(Interpreter &Interp,
-                                     std::unique_ptr<CompilerInstance> Instance,
-                                     llvm::LLVMContext &LLVMCtx,
+IncrementalParser::IncrementalParser(std::unique_ptr<CompilerInstance> Instance,
                                      llvm::Error &Err)
     : CI(std::move(Instance)) {
   llvm::ErrorAsOutParameter EAO(&Err);
-  Act = std::make_unique<IncrementalAction>(*CI, LLVMCtx, Err);
-  if (Err)
-    return;
-  CI->ExecuteAction(*Act);
 
-  if (getCodeGen())
-    CachedInCodeGenModule = GenModule();
-
-  std::unique_ptr<ASTConsumer> IncrConsumer =
-      std::make_unique<IncrementalASTConsumer>(Interp, CI->takeASTConsumer());
-  CI->setASTConsumer(std::move(IncrConsumer));
   Consumer = &CI->getASTConsumer();
   P.reset(
       new Parser(CI->getPreprocessor(), CI->getSema(), /*SkipBodies=*/false));
   P->Initialize();
-
-  // An initial PTU is needed as CUDA includes some headers automatically
-  auto PTU = ParseOrWrapTopLevelDecl();
-  if (auto E = PTU.takeError()) {
-    consumeError(std::move(E)); // FIXME
-    return;                     // PTU.takeError();
-  }
-
-  if (getCodeGen()) {
-    PTU->TheModule = GenModule();
-    assert(PTU->TheModule && "Failed to create initial PTU");
-  }
 }
 
-IncrementalParser::~IncrementalParser() {
-  P.reset();
-  Act->FinalizeAction();
-}
+IncrementalParser::~IncrementalParser() { P.reset(); }
 
-llvm::Expected<PartialTranslationUnit &>
+llvm::Expected<TranslationUnitDecl *>
 IncrementalParser::ParseOrWrapTopLevelDecl() {
   // Recover resources if we crash before exiting this method.
   Sema &S = CI->getSema();
@@ -247,12 +47,9 @@ IncrementalParser::ParseOrWrapTopLevelDecl() {
   Sema::GlobalEagerInstantiationScope GlobalInstantiations(S, /*Enabled=*/true);
   Sema::LocalEagerInstantiationScope LocalInstantiations(S);
 
-  PTUs.emplace_back(PartialTranslationUnit());
-  PartialTranslationUnit &LastPTU = PTUs.back();
   // Add a new PTU.
   ASTContext &C = S.getASTContext();
   C.addTranslationUnitDecl();
-  LastPTU.TUPart = C.getTranslationUnitDecl();
 
   // Skip previous eof due to last incremental input.
   if (P->getCurToken().is(tok::annot_repl_input_end)) {
@@ -278,9 +75,7 @@ IncrementalParser::ParseOrWrapTopLevelDecl() {
 
   DiagnosticsEngine &Diags = getCI()->getDiagnostics();
   if (Diags.hasErrorOccurred()) {
-    PartialTranslationUnit MostRecentPTU = {C.getTranslationUnitDecl(),
-                                            nullptr};
-    CleanUpPTU(MostRecentPTU);
+    CleanUpPTU(C.getTranslationUnitDecl());
 
     Diags.Reset(/*soft=*/true);
     Diags.getClient()->clear();
@@ -299,10 +94,10 @@ IncrementalParser::ParseOrWrapTopLevelDecl() {
 
   Consumer->HandleTranslationUnit(C);
 
-  return LastPTU;
+  return C.getTranslationUnitDecl();
 }
 
-llvm::Expected<PartialTranslationUnit &>
+llvm::Expected<TranslationUnitDecl *>
 IncrementalParser::Parse(llvm::StringRef input) {
   Preprocessor &PP = CI->getPreprocessor();
   assert(PP.isIncrementalProcessingEnabled() && "Not in incremental mode!?");
@@ -356,37 +151,10 @@ IncrementalParser::Parse(llvm::StringRef input) {
            "Lexer must be EOF when starting incremental parse!");
   }
 
-  if (std::unique_ptr<llvm::Module> M = GenModule())
-    PTU->TheModule = std::move(M);
-
   return PTU;
 }
 
-std::unique_ptr<llvm::Module> IncrementalParser::GenModule() {
-  static unsigned ID = 0;
-  if (CodeGenerator *CG = getCodeGen()) {
-    // Clang's CodeGen is designed to work with a single llvm::Module. In many
-    // cases for convenience various CodeGen parts have a reference to the
-    // llvm::Module (TheModule or Module) which does not change when a new
-    // module is pushed. However, the execution engine wants to take ownership
-    // of the module which does not map well to CodeGen's design. To work this
-    // around we created an empty module to make CodeGen happy. We should make
-    // sure it always stays empty.
-    assert((!CachedInCodeGenModule ||
-            (CachedInCodeGenModule->empty() &&
-             CachedInCodeGenModule->global_empty() &&
-             CachedInCodeGenModule->alias_empty() &&
-             CachedInCodeGenModule->ifunc_empty())) &&
-           "CodeGen wrote to a readonly module");
-    std::unique_ptr<llvm::Module> M(CG->ReleaseModule());
-    CG->StartModule("incr_module_" + std::to_string(ID++), M->getContext());
-    return M;
-  }
-  return nullptr;
-}
-
-void IncrementalParser::CleanUpPTU(PartialTranslationUnit &PTU) {
-  TranslationUnitDecl *MostRecentTU = PTU.TUPart;
+void IncrementalParser::CleanUpPTU(TranslationUnitDecl *MostRecentTU) {
   if (StoredDeclsMap *Map = MostRecentTU->getPrimaryContext()->getLookupPtr()) {
     for (auto &&[Key, List] : *Map) {
       DeclContextLookupResult R = List.getLookupResult();
@@ -419,9 +187,4 @@ void IncrementalParser::CleanUpPTU(PartialTranslationUnit &PTU) {
   }
 }
 
-llvm::StringRef IncrementalParser::GetMangledName(GlobalDecl GD) const {
-  CodeGenerator *CG = getCodeGen();
-  assert(CG);
-  return CG->GetMangledName(GD);
-}
 } // end namespace clang
diff --git a/clang/lib/Interpreter/IncrementalParser.h b/clang/lib/Interpreter/IncrementalParser.h
index f63bce50acd3b9..4ce361143b581b 100644
--- a/clang/lib/Interpreter/IncrementalParser.h
+++ b/clang/lib/Interpreter/IncrementalParser.h
@@ -13,35 +13,24 @@
 #ifndef LLVM_CLANG_LIB_INTERPRETER_INCREMENTALPARSER_H
 #define LLVM_CLANG_LIB_INTERPRETER_INCREMENTALPARSER_H
 
-#include "clang/AST/GlobalDecl.h"
-#include "clang/Interpreter/PartialTranslationUnit.h"
-
-#include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Error.h"
 
 #include <list>
 #include <memory>
-namespace llvm {
-class LLVMContext;
-class Module;
-} // namespace llvm
 
 namespace clang {
 class ASTConsumer;
 class CodeGenerator;
 class CompilerInstance;
-class IncrementalAction;
-class Interpreter;
 class Parser;
+class TranslationUnitDecl;
+
 /// Provides support for incremental compilation. Keeps track of the state
 /// changes between the subsequent incremental input.
 ///
 class IncrementalParser {
 protected:
-  /// Long-lived, incremental parsing action.
-  std::unique_ptr<IncrementalAction> Act;
-
   /// Compiler instance performing the incremental compilation.
   std::unique_ptr<CompilerInstance> CI;
 
@@ -54,42 +43,24 @@ class IncrementalParser {
   /// Counts the number of direct user input lines that have been parsed.
   unsigned InputCount = 0;
 
-  /// List containing every information about every incrementally parsed piece
-  /// of code.
-  std::list<PartialTranslationUnit> PTUs;
-
-  /// When CodeGen is created the first llvm::Module gets cached in many places
-  /// and we must keep it alive.
-  std::unique_ptr<llvm::Module> CachedInCodeGenModule;
-
   IncrementalParser();
 
 public:
-  IncrementalParser(Interpreter &Interp,
-                    std::unique_ptr<CompilerInstance> Instance,
-                    llvm::LLVMContext &LLVMCtx, llvm::Error &Err);
+  IncrementalParser(std::unique_ptr<CompilerInstance> Instance,
+                    llvm::Error &Err);
   virtual ~IncrementalParser();
 
   CompilerInstance *getCI() { return CI.get(); }
-  CodeGenerator *getCodeGen() const;
 
   /// Parses incremental input by creating an in-memory file.
   ///\returns a \c PartialTranslationUnit which holds information about the
-  /// \c TranslationUnitDecl and \c llvm::Module corresponding to the input.
-  virtual llvm::Expected<PartialTranslationUnit &> Parse(llvm::StringRef Input);
-
-  /// Uses the CodeGenModule mangled name cache and avoids recomputing.
-  ///\returns the mangled name of a \c GD.
-  llvm::StringRef GetMangledName(GlobalDecl GD) const;
-
-  void CleanUpPTU(PartialTranslationUnit &PTU);
-
-  std::list<PartialTranslationUnit> &getPTUs() { return PTUs; }
+  /// \c TranslationUnitDecl.
+  virtual llvm::Expected<TranslationUnitDecl *> Parse(llvm::StringRef Input);
 
-  std::unique_ptr<llvm::Module> GenModule();
+  void CleanUpPTU(TranslationUnitDecl *MostRecentTU);
 
 private:
-  llvm::Expected<PartialTranslationUnit &> ParseOrWrapTopLevelDecl();
+  llvm::Expected<TranslationUnitDecl *> ParseOrWrapTopLevelDecl();
 };
 } // end namespace clang
 
diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp
index 7209a33272ef22..039ee4ecc99dd8 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -19,6 +19,7 @@
 #include "Wasm.h"
 #endif // __EMSCRIPTEN__
 
+#include "clang/AST/ASTConsumer.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Mangle.h"
 #include "clang/AST/TypeVisitor.h"
@@ -33,7 +34,10 @@
 #include "clang/Driver/Options.h"
 #include "clang/Driver/Tool.h"
 #include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendAction.h"
+#include "clang/Frontend/MultiplexConsumer.h"
 #include "clang/Frontend/TextDiagnosticBuffer.h"
+#include "clang/FrontendTool/Utils.h"
 #include "clang/Interpreter/Interpreter.h"
 #include "clang/Interpreter/Value.h"
 #include "clang/Lex/PreprocessorOptions.h"
@@ -50,7 +54,6 @@
 #include <cstdarg>
 
 using namespace clang;
-
 // FIXME: Figure out how to unify with namespace init_convenience from
 //        tools/clang-import-test/clang-import-test.cpp
 namespace {
@@ -138,6 +141,8 @@ CreateCI(const llvm::opt::ArgStringList &Argv) {
 
 } // anonymous namespace
 
+namespace clang {
+
 llvm::Expected<std::unique_ptr<CompilerInstance>>
 IncrementalCompilerBuilder::create(std::string TT,
                                    std::vector<const char *> &ClangArgv) {
@@ -241,20 +246,177 @@ IncrementalCompilerBuilder::CreateCudaHost() {
   return IncrementalCompilerBuilder::createCuda(false);
 }
 
+class InProcessPrintingASTConsumer final : public MultiplexConsumer {
+  Interpreter &Interp;
+
+public:
+  InProcessPrintingASTConsumer(std::unique_ptr<ASTConsumer> C, Interpreter &I)
+      : MultiplexConsumer(std::move(C)), Interp(I) {}
+  bool HandleTopLevelDecl(DeclGroupRef DGR) override final {
+    if (DGR.isNull())
+      return true;
+    // if (!Consumer)
+    //   return true;
+
+    for (Decl *D : DGR)
+      if (auto *TLSD = llvm::dyn_cast<TopLevelStmtDecl>(D))
+        if (TLSD && TLSD->isSemiMissing()) {
+          auto ExprOrErr =
+              Interp.AttachValuePrinting(cast<Expr>(TLSD->getStmt()));
+          if (llvm::Error E = ExprOrErr.takeError()) {
+            llvm::logAllUnhandledErrors(std::move(E), llvm::errs(),
+                                        "Value printing failed: ");
+            return false; // abort parsing
+          }
+          TLSD->setStmt(*ExprOrErr);
+        }
+
+    return MultiplexConsumer::HandleTopLevelDecl(DGR);
+  }
+};
+
+/// A custom action enabling the incremental processing functionality.
+///
+/// The usual \p FrontendAction expects one call to ExecuteAction and once it
+/// sees a call to \p EndSourceFile it deletes some of the important objects
+/// such as \p Preprocessor and \p Sema assuming no further input will come.
+///
+/// \p IncrementalAction ensures it keep its underlying action's objects alive
+/// as long as the \p IncrementalParser needs them.
+///
+class IncrementalAction : public WrapperFrontendAction {
+private:
+  bool IsTerminating = false;
+  Interpreter &Interp;
+  std::unique_ptr<ASTConsumer> Consumer;
+
+public:
+  IncrementalAction(CompilerInstance &CI, llvm::LLVMContext &LLVMCtx,
+                    llvm::Error &Err, Interpreter &I,
+                    std::unique_ptr<ASTConsumer> Consumer = nullptr)
+      : WrapperFrontendAction([&]() {
+          llvm::ErrorAsOutParameter EAO(&Err);
+          std::unique_ptr<FrontendAction> Act;
+          switch (CI.getFrontendOpts().ProgramAction) {
+          default:
+            Err = llvm::createStringError(
+                std::errc::state_not_recoverable,
+                "Driver initialization failed. "
+                "Incremental mode for action %d is not supported",
+                CI.getFrontendOpts().ProgramAction);
+            return Act;
+          case frontend::ASTDump:
+            [[fallthrough]];
+          case frontend::ASTPrint:
+            [[fallthrough]];
+          case frontend::ParseSyntaxOnly:
+            Act = CreateFrontendAction(CI);
+            break;
+          case frontend::PluginAction:
+            [[fallthrough]];
+          case frontend::EmitAssembly:
+            [[fallthrough]];
+          case frontend::EmitBC:
+            [[fallthrough]];
+          case frontend::EmitObj:
+            [[fallthrough]];
+          case frontend::PrintPreprocessedInput:
+            [[fallthrough]];
+          case frontend::EmitLLVMOnly:
+            Act.reset(new EmitLLVMOnlyAction(&LLVMCtx));
+            break;
+          }
+          return Act;
+        }()),
+        Interp(I), Consumer(std::move(Consumer)) {}
+  FrontendAction *getWrapped() const { return WrappedAction.get(); }
+  TranslationUnitKind getTranslationUnitKind() override {
+    return TU_Incremental;
+  }
+
+  std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
+                                                 StringRef InFile) override {
+    std::unique_ptr<ASTConsumer> C =
+        WrapperFrontendAction::CreateASTConsumer(CI, InFile);
+
+    if (Consumer) {
+      std::vector<std::unique_ptr<ASTConsumer>> Cs;
+      Cs.push_back(std::move(Consumer));
+      Cs.push_back(std::move(C));
+      return std::make_unique<MultiplexConsumer>(std::move(Cs));
+    }
+
+    return std::make_unique<InProcessPrintingASTConsumer>(std::move(C), Interp);
+  }
+
+  void ExecuteAction() override {
+    CompilerInstance &CI = getCompilerInstance();
+    assert(CI.hasPreprocessor() && "No PP!");
+
+    // Use a code completion consumer?
+    CodeCompleteConsumer *CompletionConsumer = nullptr;
+    if (CI.hasCodeCompletionConsumer())
+      CompletionConsumer = &CI.getCodeCompletionConsumer();
+
+    Preprocessor &PP = CI.getPreprocessor();
+    PP.EnterMainSourceFile();
+
+    if (!CI.hasSema())
+      CI.createSema(getTranslationUnitKind(), CompletionConsumer);
+  }
+
+  // Do not terminate after processing the input. This allows us to keep various
+  // clang objects alive and to incrementally grow the current TU.
+  void EndSourceFile() override {
+    // The WrappedAction can be nullptr if we issued an error in the ctor.
+    if (IsTerminating && getWrapped())
+      WrapperFrontendAction::EndSourceFile();
+  }
+
+  void FinalizeAction() {
+    assert(!IsTerminating && "Already finalized!");
+    IsTerminating = true;
+    EndSourceFile();
+  }
+};
+
 Interpreter::Interpreter(std::unique_ptr<CompilerInstance> CI,
                          llvm::Error &ErrOut,
-                         std::unique_ptr<llvm::orc::LLJITBuilder> JITBuilder)
+                         std::unique_ptr<llvm::orc::LLJITBuilder> JITBuilder,
+                         std::unique_ptr<clang::ASTConsumer> Consumer)
     : JITBuilder(std::move(JITBuilder)) {
   llvm::ErrorAsOutParameter EAO(&ErrOut);
   auto LLVMCtx = std::make_unique<llvm::LLVMContext>();
   TSCtx = std::make_unique<llvm::orc::ThreadSafeContext>(std::move(LLVMCtx));
-  IncrParser = std::make_unique<IncrementalParser>(
-      *this, std::move(CI), *TSCtx->getContext(), ErrOut);
+
+  Act = std::make_unique<IncrementalAction>(*CI, *TSCtx->getContext(), ErrOut,
+                                            *this, std::move(Consumer));
+  if (ErrOut)
+    return;
+  CI->ExecuteAction(*Act);
+
+  if (getCodeGen())
+    CachedInCodeGenModule = GenModule();
+
+  IncrParser = std::make_unique<IncrementalParser>(std::move(CI), ErrOut);
+
+  // An initial PTU is needed as CUDA includes some headers automatically.
+  auto PTU = Parse("");
+  if (auto E = PTU.takeError()) {
+    ErrOut = joinErrors(std::move(ErrOut), std::move(E));
+    return;
+  }
+
+  if (getCodeGen()) {
+    PTU->TheModule = GenModule();
+    assert(PTU->TheModule && "Failed to create initial PTU");
+  }
+
   if (ErrOut)
     return;
 
   // Not all frontends support code-generation, e.g. ast-dump actions don't
-  if (IncrParser->getCodeGen()) {
+  if (getCodeGen()) {
     if (llvm::Error Err = CreateExecutor()) {
       ErrOut = joinErrors(std::move(ErrOut), std::move(Err));
       return;
@@ -262,7 +424,7 @@ Interpreter::Interpreter(std::unique_ptr<CompilerInstance> CI,
 
     // Process the PTUs that came from initialization. For example -include will
     // give us a header that's processed at initialization of the preprocessor.
-    for (PartialTranslationUnit &PTU : IncrParser->getPTUs())
+    for (PartialTranslationUnit &PTU : PTUs)
       if (llvm::Error Err = Execute(PTU)) {
         ErrOut = joinErrors(std::move(ErrOut), std::move(Err));
         return;
@@ -271,6 +433,8 @@ Interpreter::Interpreter(std::unique_ptr<CompilerInstance> CI,
 }
 
 Interpreter::~Interpreter() {
+  Act->FinalizeAction();
+  IncrParser.reset();
   if (IncrExecutor) {
     if (llvm::Error Err = IncrExecutor->cleanUp())
       llvm::report_fatal_error(
@@ -320,7 +484,6 @@ Interpreter::create(std::unique_ptr<CompilerInstance> CI) {
     return PTU.takeError();
   Interp->markUserCodeStart();
 
-  Interp->ValuePrintingInfo.resize(4);
   return std::move(Interp);
 }
 
@@ -342,8 +505,8 @@ Interpreter::createWithCUDA(std::unique_ptr<CompilerInstance> CI,
 
   llvm::Error Err = llvm::Error::success();
   auto DeviceParser = std::make_unique<IncrementalCUDADeviceParser>(
-      **Interp, std::move(DCI), *(*Interp)->IncrParser.get(),
-      *(*Interp)->TSCtx->getContext(), IMVFS, Err);
+      std::move(DCI), *(*Interp)->IncrParser.get(), IMVFS, Err,
+      (*Interp)->PTUs);
   if (Err)
     return std::move(Err);
 
@@ -379,22 +542,21 @@ const ASTContext &Interpreter::getASTContext() const {
 
 void Interpreter::markUserCodeStart() {
   assert(!InitPTUSize && "We only do this once");
-  InitPTUSize = IncrParser->getPTUs().size();
+  InitPTUSize = PTUs.size();
 }
 
 size_t Interpreter::getEffectivePTUSize() const {
-  std::list<PartialTranslationUnit> &PTUs = IncrParser->getPTUs();
   assert(PTUs.size() >= InitPTUSize && "empty PTU list?");
   return PTUs.size() - InitPTUSize;
 }
 
 llvm::Expected<PartialTranslationUnit &>
 Interpreter::Parse(llvm::StringRef Code) {
-  // If we have a device parser, parse it first.
-  // The generated code will be included in the host compilation
+  // If we have a device parser, parse it first. The generated code will be
+  // included in the host compilation
   if (DeviceParser) {
-    auto DevicePTU = DeviceParser->Parse(Code);
-    if (auto E = DevicePTU.takeError())
+    llvm::Expected<TranslationUnitDecl *> DeviceTU = DeviceParser->Parse(Code);
+    if (auto E = DeviceTU.takeError())
       return std::move(E);
   }
 
@@ -402,7 +564,19 @@ Interpreter::Parse(llvm::StringRef Code) {
   // printing could cause it.
   getCompilerInstance()->getDiagnostics().setSeverity(
       clang::diag::warn_unused_expr, diag::Severity::Ignored, SourceLocation());
-  return IncrParser->Parse(Code);
+
+  llvm::Expected<TranslationUnitDecl *> TuOrErr = IncrParser->Parse(Code);
+  if (!TuOrErr)
+    return TuOrErr.takeError();
+
+  PTUs.emplace_back(PartialTranslationUnit());
+  PartialTranslationUnit &LastPTU = PTUs.back();
+  LastPTU.TUPart = *TuOrErr;
+
+  if (std::unique_ptr<llvm::Module> M = GenModule())
+    LastPTU.TheModule = std::move(M);
+
+  return LastPTU;
 }
 
 static llvm::Expected<llvm::orc::JITTargetMachineBuilder>
@@ -420,7 +594,7 @@ llvm::Error Interpreter::CreateExecutor() {
     return llvm::make_error<llvm::StringError>("Operation failed. "
                                                "Execution engine exists",
                                                std::error_code());
-  if (!IncrParser->getCodeGen())
+  if (!getCodeGen())
     return llvm::make_error<llvm::StringError>("Operation failed. "
                                                "No code generator available",
                                                std::error_code());
@@ -492,7 +666,7 @@ Interpreter::getSymbolAddress(GlobalDecl GD) const {
     return llvm::make_error<llvm::StringError>("Operation failed. "
                                                "No execution engine",
                                                std::error_code());
-  llvm::StringRef MangledName = IncrParser->GetMangledName(GD);
+  llvm::StringRef MangledName = getCodeGen()->GetMangledName(GD);
   return getSymbolAddress(MangledName);
 }
 
@@ -518,7 +692,6 @@ Interpreter::getSymbolAddressFromLinkerName(llvm::StringRef Name) const {
 
 llvm::Error Interpreter::Undo(unsigned N) {
 
-  std::list<PartialTranslationUnit> &PTUs = IncrParser->getPTUs();
   if (N > getEffectivePTUSize())
     return llvm::make_error<llvm::StringError>("Operation failed. "
                                                "Too many undos",
@@ -529,7 +702,7 @@ llvm::Error Interpreter::Undo(unsigned N) {
         return Err;
     }
 
-    IncrParser->CleanUpPTU(PTUs.back());
+    IncrParser->CleanUpPTU(PTUs.back().TUPart);
     PTUs.pop_back();
   }
   return llvm::Error::success();
@@ -551,416 +724,33 @@ llvm::Error Interpreter::LoadDynamicLibrary(const char *name) {
   return llvm::Error::success();
 }
 
-llvm::Expected<llvm::orc::ExecutorAddr>
-Interpreter::CompileDtorCall(CXXRecordDecl *CXXRD) {
-  assert(CXXRD && "Cannot compile a destructor for a nullptr");
-  if (auto Dtor = Dtors.find(CXXRD); Dtor != Dtors.end())
-    return Dtor->getSecond();
-
-  if (CXXRD->hasIrrelevantDestructor())
-    return llvm::orc::ExecutorAddr{};
-
-  CXXDestructorDecl *DtorRD =
-      getCompilerInstance()->getSema().LookupDestructor(CXXRD);
-
-  llvm::StringRef Name =
-      IncrParser->GetMangledName(GlobalDecl(DtorRD, Dtor_Base));
-  auto AddrOrErr = getSymbolAddress(Name);
-  if (!AddrOrErr)
-    return AddrOrErr.takeError();
-
-  Dtors[CXXRD] = *AddrOrErr;
-  return AddrOrErr;
-}
-
-static constexpr llvm::StringRef MagicRuntimeInterface[] = {
-    "__clang_Interpreter_SetValueNoAlloc",
-    "__clang_Interpreter_SetValueWithAlloc",
-    "__clang_Interpreter_SetValueCopyArr", "__ci_newtag"};
-
-static std::unique_ptr<RuntimeInterfaceBuilder>
-createInProcessRuntimeInterfaceBuilder(Interpreter &Interp, ASTContext &Ctx,
-                                       Sema &S);
-
-std::unique_ptr<RuntimeInterfaceBuilder> Interpreter::FindRuntimeInterface() {
-  if (llvm::all_of(ValuePrintingInfo, [](Expr *E) { return E != nullptr; }))
-    return nullptr;
-
-  Sema &S = getCompilerInstance()->getSema();
-  ASTContext &Ctx = S.getASTContext();
-
-  auto LookupInterface = [&](Expr *&Interface, llvm::StringRef Name) {
-    LookupResult R(S, &Ctx.Idents.get(Name), SourceLocation(),
-                   Sema::LookupOrdinaryName,
-                   RedeclarationKind::ForVisibleRedeclaration);
-    S.LookupQualifiedName(R, Ctx.getTranslationUnitDecl());
-    if (R.empty())
-      return false;
-
-    CXXScopeSpec CSS;
-    Interface = S.BuildDeclarationNameExpr(CSS, R, /*ADL=*/false).get();
-    return true;
-  };
-
-  if (!LookupInterface(ValuePrintingInfo[NoAlloc],
-                       MagicRuntimeInterface[NoAlloc]))
-    return nullptr;
-  if (Ctx.getLangOpts().CPlusPlus) {
-    if (!LookupInterface(ValuePrintingInfo[WithAlloc],
-                         MagicRuntimeInterface[WithAlloc]))
-      return nullptr;
-    if (!LookupInterface(ValuePrintingInfo[CopyArray],
-                         MagicRuntimeInterface[CopyArray]))
-      return nullptr;
-    if (!LookupInterface(ValuePrintingInfo[NewTag],
-                         MagicRuntimeInterface[NewTag]))
-      return nullptr;
+std::unique_ptr<llvm::Module> Interpreter::GenModule() {
+  static unsigned ID = 0;
+  if (CodeGenerator *CG = getCodeGen()) {
+    // Clang's CodeGen is designed to work with a single llvm::Module. In many
+    // cases for convenience various CodeGen parts have a reference to the
+    // llvm::Module (TheModule or Module) which does not change when a new
+    // module is pushed. However, the execution engine wants to take ownership
+    // of the module which does not map well to CodeGen's design. To work this
+    // around we created an empty module to make CodeGen happy. We should make
+    // sure it always stays empty.
+    assert((!CachedInCodeGenModule || (CachedInCodeGenModule->empty() &&
+                                       CachedInCodeGenModule->global_empty() &&
+                                       CachedInCodeGenModule->alias_empty() &&
+                                       CachedInCodeGenModule->ifunc_empty())) &&
+           "CodeGen wrote to a readonly module");
+    std::unique_ptr<llvm::Module> M(CG->ReleaseModule());
+    CG->StartModule("incr_module_" + std::to_string(ID++), M->getContext());
+    return M;
   }
-
-  return createInProcessRuntimeInterfaceBuilder(*this, Ctx, S);
+  return nullptr;
 }
 
-namespace {
-
-class InterfaceKindVisitor
-    : public TypeVisitor<InterfaceKindVisitor, Interpreter::InterfaceKind> {
-  friend class InProcessRuntimeInterfaceBuilder;
-
-  ASTContext &Ctx;
-  Sema &S;
-  Expr *E;
-  llvm::SmallVector<Expr *, 3> Args;
-
-public:
-  InterfaceKindVisitor(ASTContext &Ctx, Sema &S, Expr *E)
-      : Ctx(Ctx), S(S), E(E) {}
-
-  Interpreter::InterfaceKind VisitRecordType(const RecordType *Ty) {
-    return Interpreter::InterfaceKind::WithAlloc;
-  }
-
-  Interpreter::InterfaceKind
-  VisitMemberPointerType(const MemberPointerType *Ty) {
-    return Interpreter::InterfaceKind::WithAlloc;
-  }
-
-  Interpreter::InterfaceKind
-  VisitConstantArrayType(const ConstantArrayType *Ty) {
-    return Interpreter::InterfaceKind::CopyArray;
-  }
-
-  Interpreter::InterfaceKind
-  VisitFunctionProtoType(const FunctionProtoType *Ty) {
-    HandlePtrType(Ty);
-    return Interpreter::InterfaceKind::NoAlloc;
-  }
-
-  Interpreter::InterfaceKind VisitPointerType(const PointerType *Ty) {
-    HandlePtrType(Ty);
-    return Interpreter::InterfaceKind::NoAlloc;
-  }
-
-  Interpreter::InterfaceKind VisitReferenceType(const ReferenceType *Ty) {
-    ExprResult AddrOfE = S.CreateBuiltinUnaryOp(SourceLocation(), UO_AddrOf, E);
-    assert(!AddrOfE.isInvalid() && "Can not create unary expression");
-    Args.push_back(AddrOfE.get());
-    return Interpreter::InterfaceKind::NoAlloc;
-  }
-
-  Interpreter::InterfaceKind VisitBuiltinType(const BuiltinType *Ty) {
-    if (Ty->isNullPtrType())
-      Args.push_back(E);
-    else if (Ty->isFloatingType())
-      Args.push_back(E);
-    else if (Ty->isIntegralOrEnumerationType())
-      HandleIntegralOrEnumType(Ty);
-    else if (Ty->isVoidType()) {
-      // Do we need to still run `E`?
-    }
-
-    return Interpreter::InterfaceKind::NoAlloc;
-  }
-
-  Interpreter::InterfaceKind VisitEnumType(const EnumType *Ty) {
-    HandleIntegralOrEnumType(Ty);
-    return Interpreter::InterfaceKind::NoAlloc;
-  }
-
-private:
-  // Force cast these types to the uint that fits the register size. That way we
-  // reduce the number of overloads of `__clang_Interpreter_SetValueNoAlloc`.
-  void HandleIntegralOrEnumType(const Type *Ty) {
-    uint64_t PtrBits = Ctx.getTypeSize(Ctx.VoidPtrTy);
-    QualType UIntTy = Ctx.getBitIntType(/*Unsigned=*/true, PtrBits);
-    TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(UIntTy);
-    ExprResult CastedExpr =
-        S.BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), E);
-    assert(!CastedExpr.isInvalid() && "Cannot create cstyle cast expr");
-    Args.push_back(CastedExpr.get());
-  }
-
-  void HandlePtrType(const Type *Ty) {
-    TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ctx.VoidPtrTy);
-    ExprResult CastedExpr =
-        S.BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), E);
-    assert(!CastedExpr.isInvalid() && "Can not create cstyle cast expression");
-    Args.push_back(CastedExpr.get());
-  }
-};
-
-class InProcessRuntimeInterfaceBuilder : public RuntimeInterfaceBuilder {
-  Interpreter &Interp;
-  ASTContext &Ctx;
-  Sema &S;
-
-public:
-  InProcessRuntimeInterfaceBuilder(Interpreter &Interp, ASTContext &C, Sema &S)
-      : Interp(Interp), Ctx(C), S(S) {}
-
-  TransformExprFunction *getPrintValueTransformer() override {
-    return &transformForValuePrinting;
-  }
-
-private:
-  static ExprResult transformForValuePrinting(RuntimeInterfaceBuilder *Builder,
-                                              Expr *E,
-                                              ArrayRef<Expr *> FixedArgs) {
-    auto *B = static_cast<InProcessRuntimeInterfaceBuilder *>(Builder);
-
-    // Get rid of ExprWithCleanups.
-    if (auto *EWC = llvm::dyn_cast_if_present<ExprWithCleanups>(E))
-      E = EWC->getSubExpr();
-
-    InterfaceKindVisitor Visitor(B->Ctx, B->S, E);
-
-    // The Interpreter* parameter and the out parameter `OutVal`.
-    for (Expr *E : FixedArgs)
-      Visitor.Args.push_back(E);
-
-    QualType Ty = E->getType();
-    QualType DesugaredTy = Ty.getDesugaredType(B->Ctx);
-
-    // For lvalue struct, we treat it as a reference.
-    if (DesugaredTy->isRecordType() && E->isLValue()) {
-      DesugaredTy = B->Ctx.getLValueReferenceType(DesugaredTy);
-      Ty = B->Ctx.getLValueReferenceType(Ty);
-    }
-
-    Expr *TypeArg = CStyleCastPtrExpr(B->S, B->Ctx.VoidPtrTy,
-                                      (uintptr_t)Ty.getAsOpaquePtr());
-    // The QualType parameter `OpaqueType`, represented as `void*`.
-    Visitor.Args.push_back(TypeArg);
-
-    // We push the last parameter based on the type of the Expr. Note we need
-    // special care for rvalue struct.
-    Interpreter::InterfaceKind Kind = Visitor.Visit(&*DesugaredTy);
-    switch (Kind) {
-    case Interpreter::InterfaceKind::WithAlloc:
-    case Interpreter::InterfaceKind::CopyArray: {
-      // __clang_Interpreter_SetValueWithAlloc.
-      ExprResult AllocCall = B->S.ActOnCallExpr(
-          /*Scope=*/nullptr,
-          B->Interp
-              .getValuePrintingInfo()[Interpreter::InterfaceKind::WithAlloc],
-          E->getBeginLoc(), Visitor.Args, E->getEndLoc());
-      assert(!AllocCall.isInvalid() && "Can't create runtime interface call!");
-
-      TypeSourceInfo *TSI =
-          B->Ctx.getTrivialTypeSourceInfo(Ty, SourceLocation());
-
-      // Force CodeGen to emit destructor.
-      if (auto *RD = Ty->getAsCXXRecordDecl()) {
-        auto *Dtor = B->S.LookupDestructor(RD);
-        Dtor->addAttr(UsedAttr::CreateImplicit(B->Ctx));
-        B->Interp.getCompilerInstance()->getASTConsumer().HandleTopLevelDecl(
-            DeclGroupRef(Dtor));
-      }
-
-      // __clang_Interpreter_SetValueCopyArr.
-      if (Kind == Interpreter::InterfaceKind::CopyArray) {
-        const auto *ConstantArrTy =
-            cast<ConstantArrayType>(DesugaredTy.getTypePtr());
-        size_t ArrSize = B->Ctx.getConstantArrayElementCount(ConstantArrTy);
-        Expr *ArrSizeExpr = IntegerLiteralExpr(B->Ctx, ArrSize);
-        Expr *Args[] = {E, AllocCall.get(), ArrSizeExpr};
-        return B->S.ActOnCallExpr(
-            /*Scope *=*/nullptr,
-            B->Interp
-                .getValuePrintingInfo()[Interpreter::InterfaceKind::CopyArray],
-            SourceLocation(), Args, SourceLocation());
-      }
-      Expr *Args[] = {
-          AllocCall.get(),
-          B->Interp.getValuePrintingInfo()[Interpreter::InterfaceKind::NewTag]};
-      ExprResult CXXNewCall = B->S.BuildCXXNew(
-          E->getSourceRange(),
-          /*UseGlobal=*/true, /*PlacementLParen=*/SourceLocation(), Args,
-          /*PlacementRParen=*/SourceLocation(),
-          /*TypeIdParens=*/SourceRange(), TSI->getType(), TSI, std::nullopt,
-          E->getSourceRange(), E);
-
-      assert(!CXXNewCall.isInvalid() &&
-             "Can't create runtime placement new call!");
-
-      return B->S.ActOnFinishFullExpr(CXXNewCall.get(),
-                                      /*DiscardedValue=*/false);
-    }
-      // __clang_Interpreter_SetValueNoAlloc.
-    case Interpreter::InterfaceKind::NoAlloc: {
-      return B->S.ActOnCallExpr(
-          /*Scope=*/nullptr,
-          B->Interp.getValuePrintingInfo()[Interpreter::InterfaceKind::NoAlloc],
-          E->getBeginLoc(), Visitor.Args, E->getEndLoc());
-    }
-    default:
-      llvm_unreachable("Unhandled Interpreter::InterfaceKind");
-    }
-  }
-};
-} // namespace
-
-static std::unique_ptr<RuntimeInterfaceBuilder>
-createInProcessRuntimeInterfaceBuilder(Interpreter &Interp, ASTContext &Ctx,
-                                       Sema &S) {
-  return std::make_unique<InProcessRuntimeInterfaceBuilder>(Interp, Ctx, S);
-}
-
-// This synthesizes a call expression to a speciall
-// function that is responsible for generating the Value.
-// In general, we transform:
-//   clang-repl> x
-// To:
-//   // 1. If x is a built-in type like int, float.
-//   __clang_Interpreter_SetValueNoAlloc(ThisInterp, OpaqueValue, xQualType, x);
-//   // 2. If x is a struct, and a lvalue.
-//   __clang_Interpreter_SetValueNoAlloc(ThisInterp, OpaqueValue, xQualType,
-//   &x);
-//   // 3. If x is a struct, but a rvalue.
-//   new (__clang_Interpreter_SetValueWithAlloc(ThisInterp, OpaqueValue,
-//   xQualType)) (x);
-
-Expr *Interpreter::SynthesizeExpr(Expr *E) {
-  Sema &S = getCompilerInstance()->getSema();
-  ASTContext &Ctx = S.getASTContext();
-
-  if (!RuntimeIB) {
-    RuntimeIB = FindRuntimeInterface();
-    AddPrintValueCall = RuntimeIB->getPrintValueTransformer();
-  }
-
-  assert(AddPrintValueCall &&
-         "We don't have a runtime interface for pretty print!");
-
-  // Create parameter `ThisInterp`.
-  auto *ThisInterp = CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)this);
-
-  // Create parameter `OutVal`.
-  auto *OutValue = CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)&LastValue);
-
-  // Build `__clang_Interpreter_SetValue*` call.
-  ExprResult Result =
-      AddPrintValueCall(RuntimeIB.get(), E, {ThisInterp, OutValue});
-
-  // It could fail, like printing an array type in C. (not supported)
-  if (Result.isInvalid())
-    return E;
-  return Result.get();
-}
-
-// Temporary rvalue struct that need special care.
-REPL_EXTERNAL_VISIBILITY void *
-__clang_Interpreter_SetValueWithAlloc(void *This, void *OutVal,
-                                      void *OpaqueType) {
-  Value &VRef = *(Value *)OutVal;
-  VRef = Value(static_cast<Interpreter *>(This), OpaqueType);
-  return VRef.getPtr();
-}
-
-extern "C" void REPL_EXTERNAL_VISIBILITY __clang_Interpreter_SetValueNoAlloc(
-    void *This, void *OutVal, void *OpaqueType, ...) {
-  Value &VRef = *(Value *)OutVal;
-  Interpreter *I = static_cast<Interpreter *>(This);
-  VRef = Value(I, OpaqueType);
-  if (VRef.isVoid())
-    return;
-
-  va_list args;
-  va_start(args, /*last named param*/ OpaqueType);
-
-  QualType QT = VRef.getType();
-  if (VRef.getKind() == Value::K_PtrOrObj) {
-    VRef.setPtr(va_arg(args, void *));
-  } else {
-    if (const auto *ET = QT->getAs<EnumType>())
-      QT = ET->getDecl()->getIntegerType();
-    switch (QT->castAs<BuiltinType>()->getKind()) {
-    default:
-      llvm_unreachable("unknown type kind!");
-      break;
-      // Types shorter than int are resolved as int, else va_arg has UB.
-    case BuiltinType::Bool:
-      VRef.setBool(va_arg(args, int));
-      break;
-    case BuiltinType::Char_S:
-      VRef.setChar_S(va_arg(args, int));
-      break;
-    case BuiltinType::SChar:
-      VRef.setSChar(va_arg(args, int));
-      break;
-    case BuiltinType::Char_U:
-      VRef.setChar_U(va_arg(args, unsigned));
-      break;
-    case BuiltinType::UChar:
-      VRef.setUChar(va_arg(args, unsigned));
-      break;
-    case BuiltinType::Short:
-      VRef.setShort(va_arg(args, int));
-      break;
-    case BuiltinType::UShort:
-      VRef.setUShort(va_arg(args, unsigned));
-      break;
-    case BuiltinType::Int:
-      VRef.setInt(va_arg(args, int));
-      break;
-    case BuiltinType::UInt:
-      VRef.setUInt(va_arg(args, unsigned));
-      break;
-    case BuiltinType::Long:
-      VRef.setLong(va_arg(args, long));
-      break;
-    case BuiltinType::ULong:
-      VRef.setULong(va_arg(args, unsigned long));
-      break;
-    case BuiltinType::LongLong:
-      VRef.setLongLong(va_arg(args, long long));
-      break;
-    case BuiltinType::ULongLong:
-      VRef.setULongLong(va_arg(args, unsigned long long));
-      break;
-      // Types shorter than double are resolved as double, else va_arg has UB.
-    case BuiltinType::Float:
-      VRef.setFloat(va_arg(args, double));
-      break;
-    case BuiltinType::Double:
-      VRef.setDouble(va_arg(args, double));
-      break;
-    case BuiltinType::LongDouble:
-      VRef.setLongDouble(va_arg(args, long double));
-      break;
-      // See REPL_BUILTIN_TYPES.
-    }
-  }
-  va_end(args);
+CodeGenerator *Interpreter::getCodeGen() const {
+  FrontendAction *WrappedAct = Act->getWrapped();
+  if (!WrappedAct->hasIRSupport())
+    return nullptr;
+  return static_cast<CodeGenAction *>(WrappedAct)->getCodeGenerator();
 }
 
-// A trampoline to work around the fact that operator placement new cannot
-// really be forward declared due to libc++ and libstdc++ declaration mismatch.
-// FIXME: __clang_Interpreter_NewTag is ODR violation because we get the same
-// definition in the interpreter runtime. We should move it in a runtime header
-// which gets included by the interpreter and here.
-struct __clang_Interpreter_NewTag {};
-REPL_EXTERNAL_VISIBILITY void *
-operator new(size_t __sz, void *__p, __clang_Interpreter_NewTag) noexcept {
-  // Just forward to the standard operator placement new.
-  return operator new(__sz, __p);
-}
+} // end namespace clang
diff --git a/clang/lib/Interpreter/InterpreterUtils.cpp b/clang/lib/Interpreter/InterpreterUtils.cpp
index 45f6322b8461ed..2cfbb2fcb45063 100644
--- a/clang/lib/Interpreter/InterpreterUtils.cpp
+++ b/clang/lib/Interpreter/InterpreterUtils.cpp
@@ -81,7 +81,7 @@ NamedDecl *LookupNamed(Sema &S, llvm::StringRef Name,
   else {
     const DeclContext *PrimaryWithin = nullptr;
     if (const auto *TD = dyn_cast<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
diff --git a/clang/lib/Interpreter/InterpreterUtils.h b/clang/lib/Interpreter/InterpreterUtils.h
index c7b405b486d936..4b92cf0c4bf978 100644
--- a/clang/lib/Interpreter/InterpreterUtils.h
+++ b/clang/lib/Interpreter/InterpreterUtils.h
@@ -47,7 +47,25 @@ NamespaceDecl *LookupNamespace(Sema &S, llvm::StringRef Name,
 NamedDecl *LookupNamed(Sema &S, llvm::StringRef Name,
                        const DeclContext *Within);
 
+NestedNameSpecifier *CreateNestedNameSpecifier(const ASTContext &Ctx,
+                                               const NamespaceDecl *Namesp);
+
+NestedNameSpecifier *CreateNestedNameSpecifier(const ASTContext &Ctx,
+                                               const TypedefNameDecl *TD,
+                                               bool FullyQualify);
+
+NestedNameSpecifier *CreateNestedNameSpecifier(const ASTContext &Ctx,
+                                               const TagDecl *TD,
+                                               bool FullyQualify);
+
+QualType GetFullyQualifiedType(QualType QT, const ASTContext &Ctx);
+
 std::string GetFullTypeName(ASTContext &Ctx, QualType QT);
+
+class Value;
+
+std::string ReplPrintTypeImpl(const Value &);
+std::string ReplPrintDataImpl(const Value &);
 } // namespace clang
 
 #endif
diff --git a/clang/lib/Interpreter/InterpreterValuePrinter.cpp b/clang/lib/Interpreter/InterpreterValuePrinter.cpp
new file mode 100644
index 00000000000000..a622d681a96f84
--- /dev/null
+++ b/clang/lib/Interpreter/InterpreterValuePrinter.cpp
@@ -0,0 +1,890 @@
+//===--- InterpreterValuePrinter.cpp - Value printing utils -----*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements routines for in-process value printing in clang-repl.
+//
+//===----------------------------------------------------------------------===//
+
+#include "IncrementalParser.h"
+#include "InterpreterUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/PrettyPrinter.h"
+#include "clang/AST/Type.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Interpreter/Interpreter.h"
+#include "clang/Interpreter/Value.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Sema/Lookup.h"
+#include "clang/Sema/Sema.h"
+
+#include "llvm/Support/Error.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <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) {
+  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))
+    return PrintDeclType(NonRefTy, TTy->getDecl());
+
+  if (const auto *TRy = dyn_cast<RecordType>(NonRefTy))
+    return PrintDeclType(NonRefTy, TRy->getDecl());
+
+  const QualType Canon = NonRefTy.getCanonicalType();
+
+  // FIXME: How a builtin type can be a function pointer type?
+  if (Canon->isBuiltinType() && !NonRefTy->isFunctionPointerType() &&
+      !NonRefTy->isMemberPointerType())
+    return Canon.getAsString(Ctx.getPrintingPolicy());
+
+  if (const auto *TDTy = dyn_cast<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))
+      return GetFullTypeName(Ctx, Canon);
+    else if (llvm::isa<TemplateSpecializationType>(SSDesugar))
+      return GetFullTypeName(Ctx, NonRefTy);
+    return PrintDeclType(NonRefTy, TDTy->getDecl());
+  }
+  return GetFullTypeName(Ctx, NonRefTy);
+}
+
+static std::string PrintEnum(const Value &V) {
+  std::string Str;
+  llvm::raw_string_ostream SS(Str);
+  ASTContext &Ctx = const_cast<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<64> APStr;
+  AP.toString(APStr, /*Radix=*/10);
+  SS << " : " << PrintQualType(Ctx, ED->getIntegerType()) << " " << APStr;
+  return Str;
+}
+
+static std::string PrintFunction(const Value &V, const void *Ptr) {
+  std::string Str;
+  llvm::raw_string_ostream SS(Str);
+  SS << "Function @" << Ptr;
+
+  const FunctionDecl *FD = nullptr;
+
+  auto Decls = V.getASTContext().getTranslationUnitDecl()->decls();
+  assert(std::distance(Decls.begin(), Decls.end()) == 1 &&
+         "TU should only contain one Decl");
+  auto *TLSD = llvm::cast<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 gonna do this, better make sure the end is valid too
+  // FIXME: getpagesize() & GetSystemInfo().dwPageSize might be better
+  static constexpr auto PAGE_SIZE = 1024;
+  while (N > 1024) {
+    N -= PAGE_SIZE;
+    End = Start + N;
+  }
+
+  if (*Start == 0)
+    return "\"\"";
+
+  // Copy the bytes until we get a null-terminator
+  SS << "\"";
+  while (Start < End && *Start)
+    SS << *Start++;
+  SS << "\"";
+
+  return Str;
+}
+
+// Build the CallExpr to `PrintValueRuntime`.
+static void BuildWrapperBody(Interpreter &Interp, Sema &S, ASTContext &Ctx,
+                             FunctionDecl *WrapperFD, QualType QT,
+                             const void *ValPtr) {
+  Sema::SynthesizedFunctionScope SemaFScope(S, WrapperFD);
+  clang::DeclarationName RuntimeCallName;
+  if (Ctx.getLangOpts().CPlusPlus)
+    RuntimeCallName = S.PP.getIdentifierInfo("PrintValueRuntime");
+  else
+    RuntimeCallName =
+        S.PP.getIdentifierInfo("caas__runtime__PrintValueRuntime");
+
+  clang::LookupResult R(S, RuntimeCallName, SourceLocation(),
+                        clang::Sema::LookupOrdinaryName);
+  S.LookupName(R, S.getCurScope());
+
+  Expr *OverldExpr = UnresolvedLookupExpr::Create(
+      Ctx, /*NamingClass=*/nullptr, NestedNameSpecifierLoc(),
+      clang::DeclarationNameInfo(RuntimeCallName, SourceLocation()),
+      /*RequiresADL=*/false, R.begin(), R.end(), /*KnownDependent=*/false,
+      /*KnownInstantiationDependent=*/false);
+
+  if (const auto *AT = llvm::dyn_cast<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();
+  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()) {
+    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 =
+        Interp.getSymbolAddressFromLinkerName(ASTNameGen.getName(D));
+
+    return AddrOrErr;
+  }
+  return llvm::orc::ExecutorAddr{};
+}
+
+static std::string CreateUniqName(std::string Base) {
+  static size_t I = 0;
+  Base += std::to_string(I);
+  I += 1;
+  return Base;
+}
+
+REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const void *Ptr) {
+  return PrintAddress(Ptr, '@');
+}
+
+REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const void **Ptr) {
+  return PrintAddress(*Ptr, '@');
+}
+
+REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const bool *Val) {
+  if (*Val)
+    return "true";
+  return "false";
+}
+
+REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const char *Val) {
+  return PrintOneChar(*Val);
+}
+
+REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const signed char *Val) {
+  return PrintOneChar(*Val);
+}
+
+REPL_EXTERNAL_VISIBILITY std::string
+PrintValueRuntime(const unsigned char *Val) {
+  return PrintOneChar(*Val);
+}
+
+REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const short *Val) {
+  std::string Str;
+  llvm::raw_string_ostream SS(Str);
+  SS << *Val;
+  return Str;
+}
+
+REPL_EXTERNAL_VISIBILITY
+std::string PrintValueRuntime(const unsigned short *Val) {
+  std::string Str;
+  llvm::raw_string_ostream SS(Str);
+  SS << *Val;
+  return Str;
+}
+
+REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const int *Val) {
+  std::string Str;
+  llvm::raw_string_ostream SS(Str);
+  SS << *Val;
+  return Str;
+}
+
+REPL_EXTERNAL_VISIBILITY
+std::string PrintValueRuntime(const unsigned int *Val) {
+  std::string Str;
+  llvm::raw_string_ostream SS(Str);
+  SS << *Val;
+  return Str;
+}
+
+REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const long *Val) {
+  std::string Str;
+  llvm::raw_string_ostream SS(Str);
+  SS << *Val;
+  return Str;
+}
+
+REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const long long *Val) {
+  std::string Str;
+  llvm::raw_string_ostream SS(Str);
+  SS << *Val;
+  return Str;
+}
+
+REPL_EXTERNAL_VISIBILITY
+std::string PrintValueRuntime(const unsigned long *Val) {
+  std::string Str;
+  llvm::raw_string_ostream SS(Str);
+  SS << *Val;
+  return Str;
+}
+
+REPL_EXTERNAL_VISIBILITY
+std::string PrintValueRuntime(const unsigned long long *Val) {
+  std::string Str;
+  llvm::raw_string_ostream SS(Str);
+  SS << *Val;
+  return Str;
+}
+
+REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const float *Val) {
+  std::string Str;
+  llvm::raw_string_ostream SS(Str);
+  SS << llvm::format("%#.6g", *Val) << 'f';
+  return Str;
+}
+
+REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const double *Val) {
+  std::string Str;
+  llvm::raw_string_ostream SS(Str);
+  SS << llvm::format("%#.12g", *Val);
+  return Str;
+}
+
+REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const long double *Val) {
+  std::string Str;
+  llvm::raw_string_ostream SS(Str);
+  SS << llvm::format("%#.8Lg", *Val) << 'L';
+  return Str;
+}
+
+REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const char *const *Val) {
+  return PrintString(Val);
+}
+
+REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const char **Val) {
+  return PrintString(Val);
+}
+
+namespace clang {
+std::string Interpreter::ValueDataToString(const Value &V) {
+  QualType QT = V.getType();
+  QualType DesugaredTy = QT.getDesugaredType(V.getASTContext());
+  QualType NonRefTy = DesugaredTy.getNonReferenceType();
+
+  if (NonRefTy->isEnumeralType())
+    return PrintEnum(V);
+
+  if (NonRefTy->isFunctionType())
+    return PrintFunction(V, &V);
+
+  if ((NonRefTy->isPointerType() || NonRefTy->isMemberPointerType()) &&
+      NonRefTy->getPointeeType()->isFunctionProtoType())
+    return PrintFunction(V, V.getPtr());
+
+  if (NonRefTy->isNullPtrType())
+    return "nullptr\n";
+
+  // If it is a builtin type dispatch to the builtin overloads.
+  if (auto *BT = DesugaredTy.getCanonicalType()->getAs<BuiltinType>()) {
+    switch (BT->getKind()) {
+    default:
+      return "{ unknown builtin type: }" + std::to_string(BT->getKind());
+#define X(type, name)                                                          \
+  case clang::BuiltinType::name: {                                             \
+    type val = V.get##name();                                                  \
+    return PrintValueRuntime(&val);                                            \
+  }
+      REPL_BUILTIN_TYPES
+#undef X
+    }
+  }
+  if (auto *CXXRD = NonRefTy->getAsCXXRecordDecl())
+    if (CXXRD->isLambda())
+      return PrintAddress(V.getPtr(), '@');
+
+  // All fails then generate a runtime call, this is slow.
+  Sema &S = getCompilerInstance()->getSema();
+  ASTContext &Ctx = S.getASTContext();
+
+  QualType RetTy;
+  if (Ctx.getLangOpts().CPlusPlus && !StdString) {
+
+    // Only include the header on demand because it's very heavy.
+    if (llvm::Error E = ParseAndExecute(
+            "#include <__clang_interpreter_runtime_printvalue.h>")) {
+      llvm::logAllUnhandledErrors(std::move(E), llvm::errs(), "Parsing failed");
+      return "{Internal error}";
+    }
+
+    // Find and cache std::string.
+    NamespaceDecl *Std = LookupNamespace(S, "std");
+    assert(Std && "Cannot find namespace std");
+    Decl *StdStringDecl = LookupNamed(S, "string", Std);
+    assert(StdStringDecl && "Cannot find std::string");
+    const auto *StdStringTyDecl = llvm::dyn_cast<TypeDecl>(StdStringDecl);
+    assert(StdStringTyDecl && "Cannot find type of std::string");
+    RetTy = QualType(StdStringTyDecl->getTypeForDecl(), /*Quals=*/0);
+  } else {
+    RetTy = Ctx.getPointerType(Ctx.CharTy.withConst());
+  }
+
+  // Create the wrapper function.
+  DeclarationName DeclName = &Ctx.Idents.get(CreateUniqName(WrapperName));
+  QualType FnTy =
+      Ctx.getFunctionType(RetTy, {}, FunctionProtoType::ExtProtoInfo());
+  auto *WrapperFD = FunctionDecl::Create(
+      Ctx, Ctx.getTranslationUnitDecl(), SourceLocation(), SourceLocation(),
+      DeclName, FnTy, Ctx.getTrivialTypeSourceInfo(FnTy), SC_None);
+
+  void *ValPtr = V.getPtr();
+
+  // FIXME: We still need to understand why we have to get the pointer to the
+  // underlying Value storage for this to work reliabily...
+  if (!V.isManuallyAlloc())
+    ValPtr = V.getPtrAddress();
+
+  BuildWrapperBody(*this, S, Ctx, WrapperFD, V.getType(), ValPtr);
+
+  auto AddrOrErr = CompileDecl(*this, WrapperFD);
+  if (!AddrOrErr)
+    llvm::logAllUnhandledErrors(AddrOrErr.takeError(), llvm::errs(),
+                                "Fail to get symbol address");
+  if (auto *Main = AddrOrErr->toPtr<std::string (*)()>())
+    return (*Main)();
+  return "Unable to print the value!";
+}
+
+std::string Interpreter::ValueTypeToString(const Value &V) const {
+  ASTContext &Ctx = const_cast<ASTContext &>(V.getASTContext());
+  QualType QT = V.getType();
+
+  std::string QTStr = PrintQualType(Ctx, QT);
+
+  if (QT->isReferenceType())
+    QTStr += " &";
+
+  return QTStr;
+}
+
+llvm::Expected<llvm::orc::ExecutorAddr>
+Interpreter::CompileDtorCall(CXXRecordDecl *CXXRD) {
+  assert(CXXRD && "Cannot compile a destructor for a nullptr");
+  if (auto Dtor = Dtors.find(CXXRD); Dtor != Dtors.end())
+    return Dtor->getSecond();
+
+  if (CXXRD->hasIrrelevantDestructor())
+    return llvm::orc::ExecutorAddr{};
+
+  CXXDestructorDecl *DtorRD =
+      getCompilerInstance()->getSema().LookupDestructor(CXXRD);
+
+  llvm::StringRef Name =
+      getCodeGen()->GetMangledName(GlobalDecl(DtorRD, Dtor_Base));
+  auto AddrOrErr = getSymbolAddress(Name);
+  if (!AddrOrErr)
+    return AddrOrErr.takeError();
+
+  Dtors[CXXRD] = *AddrOrErr;
+  return AddrOrErr;
+}
+
+enum InterfaceKind { NoAlloc, WithAlloc, CopyArray, NewTag };
+
+class InterfaceKindVisitor
+    : public TypeVisitor<InterfaceKindVisitor, InterfaceKind> {
+
+  Sema &S;
+  Expr *E;
+  llvm::SmallVectorImpl<Expr *> &Args;
+
+public:
+  InterfaceKindVisitor(Sema &S, Expr *E, llvm::SmallVectorImpl<Expr *> &Args)
+      : S(S), E(E), Args(Args) {}
+
+  InterfaceKind computeInterfaceKind(QualType Ty) {
+    return Visit(Ty.getTypePtr());
+  }
+
+  InterfaceKind VisitRecordType(const RecordType *Ty) {
+    return InterfaceKind::WithAlloc;
+  }
+
+  InterfaceKind VisitMemberPointerType(const MemberPointerType *Ty) {
+    return InterfaceKind::WithAlloc;
+  }
+
+  InterfaceKind VisitConstantArrayType(const ConstantArrayType *Ty) {
+    return InterfaceKind::CopyArray;
+  }
+
+  InterfaceKind VisitFunctionProtoType(const FunctionProtoType *Ty) {
+    HandlePtrType(Ty);
+    return InterfaceKind::NoAlloc;
+  }
+
+  InterfaceKind VisitPointerType(const PointerType *Ty) {
+    HandlePtrType(Ty);
+    return InterfaceKind::NoAlloc;
+  }
+
+  InterfaceKind VisitReferenceType(const ReferenceType *Ty) {
+    ExprResult AddrOfE = S.CreateBuiltinUnaryOp(SourceLocation(), UO_AddrOf, E);
+    assert(!AddrOfE.isInvalid() && "Can not create unary expression");
+    Args.push_back(AddrOfE.get());
+    return InterfaceKind::NoAlloc;
+  }
+
+  InterfaceKind VisitBuiltinType(const BuiltinType *Ty) {
+    if (Ty->isNullPtrType())
+      Args.push_back(E);
+    else if (Ty->isFloatingType())
+      Args.push_back(E);
+    else if (Ty->isIntegralOrEnumerationType())
+      HandleIntegralOrEnumType(Ty);
+    else if (Ty->isVoidType()) {
+      // Do we need to still run `E`?
+    }
+
+    return InterfaceKind::NoAlloc;
+  }
+
+  InterfaceKind VisitEnumType(const EnumType *Ty) {
+    HandleIntegralOrEnumType(Ty);
+    return InterfaceKind::NoAlloc;
+  }
+
+private:
+  // Force cast these types to the uint that fits the register size. That way we
+  // reduce the number of overloads of `__clang_Interpreter_SetValueNoAlloc`.
+  void HandleIntegralOrEnumType(const Type *Ty) {
+    ASTContext &Ctx = S.getASTContext();
+    uint64_t PtrBits = Ctx.getTypeSize(Ctx.VoidPtrTy);
+    QualType UIntTy = Ctx.getBitIntType(/*Unsigned=*/true, PtrBits);
+    TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(UIntTy);
+    ExprResult CastedExpr =
+        S.BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), E);
+    assert(!CastedExpr.isInvalid() && "Cannot create cstyle cast expr");
+    Args.push_back(CastedExpr.get());
+  }
+
+  void HandlePtrType(const Type *Ty) {
+    ASTContext &Ctx = S.getASTContext();
+    TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ctx.VoidPtrTy);
+    ExprResult CastedExpr =
+        S.BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), E);
+    assert(!CastedExpr.isInvalid() && "Can not create cstyle cast expression");
+    Args.push_back(CastedExpr.get());
+  }
+};
+
+// This synthesizes a call expression to a speciall
+// function that is responsible for generating the Value.
+// In general, we transform:
+//   clang-repl> x
+// To:
+//   // 1. If x is a built-in type like int, float.
+//   __clang_Interpreter_SetValueNoAlloc(ThisInterp, OpaqueValue, xQualType, x);
+//   // 2. If x is a struct, and a lvalue.
+//   __clang_Interpreter_SetValueNoAlloc(ThisInterp, OpaqueValue, xQualType,
+//   &x);
+//   // 3. If x is a struct, but a rvalue.
+//   new (__clang_Interpreter_SetValueWithAlloc(ThisInterp, OpaqueValue,
+//   xQualType)) (x);
+llvm::Expected<Expr *> Interpreter::AttachValuePrinting(Expr *E) {
+  Sema &S = getCompilerInstance()->getSema();
+  ASTContext &Ctx = S.getASTContext();
+
+  // Find the value printing builtins.
+  if (!ValuePrintingInfo[0]) {
+    assert(llvm::all_of(ValuePrintingInfo, [](Expr *E) { return !E; }));
+
+    auto LookupInterface = [&](Expr *&Interface,
+                               llvm::StringRef Name) -> llvm::Error {
+      LookupResult R(S, &Ctx.Idents.get(Name), SourceLocation(),
+                     Sema::LookupOrdinaryName,
+                     RedeclarationKind::ForVisibleRedeclaration);
+      S.LookupQualifiedName(R, Ctx.getTranslationUnitDecl());
+      if (R.empty())
+        return llvm::make_error<llvm::StringError>(
+            Name + " not found!", llvm::inconvertibleErrorCode());
+
+      CXXScopeSpec CSS;
+      Interface = S.BuildDeclarationNameExpr(CSS, R, /*ADL=*/false).get();
+      return llvm::Error::success();
+    };
+    static constexpr llvm::StringRef Builtin[] = {
+        "__clang_Interpreter_SetValueNoAlloc",
+        "__clang_Interpreter_SetValueWithAlloc",
+        "__clang_Interpreter_SetValueCopyArr", "__ci_newtag"};
+    if (llvm::Error Err =
+            LookupInterface(ValuePrintingInfo[NoAlloc], Builtin[NoAlloc]))
+      return std::move(Err);
+
+    if (Ctx.getLangOpts().CPlusPlus) {
+      if (llvm::Error Err =
+              LookupInterface(ValuePrintingInfo[WithAlloc], Builtin[WithAlloc]))
+        return std::move(Err);
+      if (llvm::Error Err =
+              LookupInterface(ValuePrintingInfo[CopyArray], Builtin[CopyArray]))
+        return std::move(Err);
+      if (llvm::Error Err =
+              LookupInterface(ValuePrintingInfo[NewTag], Builtin[NewTag]))
+        return std::move(Err);
+    }
+  }
+
+  llvm::SmallVector<Expr *, 4> AdjustedArgs;
+  // Create parameter `ThisInterp`.
+  AdjustedArgs.push_back(CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)this));
+
+  // Create parameter `OutVal`.
+  AdjustedArgs.push_back(
+      CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)&LastValue));
+
+  // Build `__clang_Interpreter_SetValue*` call.
+
+  // Get rid of ExprWithCleanups.
+  if (auto *EWC = llvm::dyn_cast_if_present<ExprWithCleanups>(E))
+    E = EWC->getSubExpr();
+
+  QualType Ty = E->getType();
+  QualType DesugaredTy = Ty.getDesugaredType(Ctx);
+
+  // For lvalue struct, we treat it as a reference.
+  if (DesugaredTy->isRecordType() && E->isLValue()) {
+    DesugaredTy = Ctx.getLValueReferenceType(DesugaredTy);
+    Ty = Ctx.getLValueReferenceType(Ty);
+  }
+
+  Expr *TypeArg =
+      CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)Ty.getAsOpaquePtr());
+  // The QualType parameter `OpaqueType`, represented as `void*`.
+  AdjustedArgs.push_back(TypeArg);
+
+  // We push the last parameter based on the type of the Expr. Note we need
+  // special care for rvalue struct.
+  InterfaceKindVisitor V(S, E, AdjustedArgs);
+  Scope *Scope = nullptr;
+  ExprResult SetValueE;
+  InterfaceKind Kind = V.computeInterfaceKind(DesugaredTy);
+  switch (Kind) {
+  case InterfaceKind::WithAlloc:
+    LLVM_FALLTHROUGH;
+  case InterfaceKind::CopyArray: {
+    // __clang_Interpreter_SetValueWithAlloc.
+    ExprResult AllocCall =
+        S.ActOnCallExpr(Scope, ValuePrintingInfo[InterfaceKind::WithAlloc],
+                        E->getBeginLoc(), AdjustedArgs, E->getEndLoc());
+    assert(!AllocCall.isInvalid() && "Can't create runtime interface call!");
+
+    TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ty, SourceLocation());
+
+    // Force CodeGen to emit destructor.
+    if (auto *RD = Ty->getAsCXXRecordDecl()) {
+      auto *Dtor = S.LookupDestructor(RD);
+      Dtor->addAttr(UsedAttr::CreateImplicit(Ctx));
+      getCompilerInstance()->getASTConsumer().HandleTopLevelDecl(
+          DeclGroupRef(Dtor));
+    }
+
+    // __clang_Interpreter_SetValueCopyArr.
+    if (Kind == InterfaceKind::CopyArray) {
+      const auto *ConstantArrTy =
+          cast<ConstantArrayType>(DesugaredTy.getTypePtr());
+      size_t ArrSize = Ctx.getConstantArrayElementCount(ConstantArrTy);
+      Expr *ArrSizeExpr = IntegerLiteralExpr(Ctx, ArrSize);
+      Expr *Args[] = {E, AllocCall.get(), ArrSizeExpr};
+      SetValueE =
+          S.ActOnCallExpr(Scope, ValuePrintingInfo[InterfaceKind::CopyArray],
+                          SourceLocation(), Args, SourceLocation());
+    }
+    Expr *Args[] = {AllocCall.get(), ValuePrintingInfo[InterfaceKind::NewTag]};
+    ExprResult CXXNewCall = S.BuildCXXNew(
+        E->getSourceRange(),
+        /*UseGlobal=*/true, /*PlacementLParen=*/SourceLocation(), Args,
+        /*PlacementRParen=*/SourceLocation(),
+        /*TypeIdParens=*/SourceRange(), TSI->getType(), TSI, std::nullopt,
+        E->getSourceRange(), E);
+
+    assert(!CXXNewCall.isInvalid() &&
+           "Can't create runtime placement new call!");
+
+    SetValueE = S.ActOnFinishFullExpr(CXXNewCall.get(),
+                                      /*DiscardedValue=*/false);
+    break;
+  }
+  // __clang_Interpreter_SetValueNoAlloc.
+  case InterfaceKind::NoAlloc: {
+    SetValueE =
+        S.ActOnCallExpr(Scope, ValuePrintingInfo[InterfaceKind::NoAlloc],
+                        E->getBeginLoc(), AdjustedArgs, E->getEndLoc());
+    break;
+  }
+  default:
+    llvm_unreachable("Unhandled InterfaceKind");
+  }
+
+  // It could fail, like printing an array type in C. (not supported)
+  if (SetValueE.isInvalid())
+    return E;
+
+  return SetValueE.get();
+}
+
+} // namespace clang
+
+// Temporary rvalue struct that need special care.
+REPL_EXTERNAL_VISIBILITY void *
+__clang_Interpreter_SetValueWithAlloc(void *This, void *OutVal,
+                                      void *OpaqueType) {
+  Value &VRef = *(Value *)OutVal;
+  VRef = Value(static_cast<Interpreter *>(This), OpaqueType);
+  return VRef.getPtr();
+}
+
+extern "C" void REPL_EXTERNAL_VISIBILITY __clang_Interpreter_SetValueNoAlloc(
+    void *This, void *OutVal, void *OpaqueType, ...) {
+  Value &VRef = *(Value *)OutVal;
+  Interpreter *I = static_cast<Interpreter *>(This);
+  VRef = Value(I, OpaqueType);
+  if (VRef.isVoid())
+    return;
+
+  va_list args;
+  va_start(args, /*last named param*/ OpaqueType);
+
+  QualType QT = VRef.getType();
+  if (VRef.getKind() == Value::K_PtrOrObj) {
+    VRef.setPtr(va_arg(args, void *));
+  } else {
+    if (const auto *ET = QT->getAs<EnumType>())
+      QT = ET->getDecl()->getIntegerType();
+    switch (QT->castAs<BuiltinType>()->getKind()) {
+    default:
+      llvm_unreachable("unknown type kind!");
+      break;
+      // Types shorter than int are resolved as int, else va_arg has UB.
+    case BuiltinType::Bool:
+      VRef.setBool(va_arg(args, int));
+      break;
+    case BuiltinType::Char_S:
+      VRef.setChar_S(va_arg(args, int));
+      break;
+    case BuiltinType::SChar:
+      VRef.setSChar(va_arg(args, int));
+      break;
+    case BuiltinType::Char_U:
+      VRef.setChar_U(va_arg(args, unsigned));
+      break;
+    case BuiltinType::UChar:
+      VRef.setUChar(va_arg(args, unsigned));
+      break;
+    case BuiltinType::Short:
+      VRef.setShort(va_arg(args, int));
+      break;
+    case BuiltinType::UShort:
+      VRef.setUShort(va_arg(args, unsigned));
+      break;
+    case BuiltinType::Int:
+      VRef.setInt(va_arg(args, int));
+      break;
+    case BuiltinType::UInt:
+      VRef.setUInt(va_arg(args, unsigned));
+      break;
+    case BuiltinType::Long:
+      VRef.setLong(va_arg(args, long));
+      break;
+    case BuiltinType::ULong:
+      VRef.setULong(va_arg(args, unsigned long));
+      break;
+    case BuiltinType::LongLong:
+      VRef.setLongLong(va_arg(args, long long));
+      break;
+    case BuiltinType::ULongLong:
+      VRef.setULongLong(va_arg(args, unsigned long long));
+      break;
+      // Types shorter than double are resolved as double, else va_arg has UB.
+    case BuiltinType::Float:
+      VRef.setFloat(va_arg(args, double));
+      break;
+    case BuiltinType::Double:
+      VRef.setDouble(va_arg(args, double));
+      break;
+    case BuiltinType::LongDouble:
+      VRef.setLongDouble(va_arg(args, long double));
+      break;
+      // See REPL_BUILTIN_TYPES.
+    }
+  }
+  va_end(args);
+}
+
+// A trampoline to work around the fact that operator placement new cannot
+// really be forward declared due to libc++ and libstdc++ declaration mismatch.
+// FIXME: __clang_Interpreter_NewTag is ODR violation because we get the same
+// definition in the interpreter runtime. We should move it in a runtime header
+// which gets included by the interpreter and here.
+struct __clang_Interpreter_NewTag {};
+REPL_EXTERNAL_VISIBILITY void *
+operator new(size_t __sz, void *__p, __clang_Interpreter_NewTag) noexcept {
+  // Just forward to the standard operator placement new.
+  return operator new(__sz, __p);
+}
diff --git a/clang/lib/Interpreter/Value.cpp b/clang/lib/Interpreter/Value.cpp
index eb2ce9c9fd3302..0de66b4f0e699b 100644
--- a/clang/lib/Interpreter/Value.cpp
+++ b/clang/lib/Interpreter/Value.cpp
@@ -12,6 +12,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/Interpreter/Value.h"
+#include "InterpreterUtils.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Type.h"
 #include "clang/Interpreter/Interpreter.h"
@@ -22,6 +23,8 @@
 #include <cstdint>
 #include <utility>
 
+using namespace clang;
+
 namespace {
 
 // This is internal buffer maintained by Value, used to hold temporaries.
@@ -231,6 +234,11 @@ void *Value::getPtr() const {
   return Data.m_Ptr;
 }
 
+void **Value::getPtrAddress() const {
+  assert(ValueKind == K_PtrOrObj);
+  return &const_cast<Value *>(this)->Data.m_Ptr;
+}
+
 QualType Value::getType() const {
   return QualType::getFromOpaquePtr(OpaqueType);
 }
@@ -253,17 +261,35 @@ const ASTContext &Value::getASTContext() const {
   return getInterpreter().getASTContext();
 }
 
-void Value::dump() const { print(llvm::outs()); }
+void Value::dump() { print(llvm::outs()); }
 
 void Value::printType(llvm::raw_ostream &Out) const {
-  Out << "Not implement yet.\n";
+  Out << Interp->ValueTypeToString(*this);
 }
-void Value::printData(llvm::raw_ostream &Out) const {
-  Out << "Not implement yet.\n";
+
+void Value::printData(llvm::raw_ostream &Out) {
+  Out << Interp->ValueDataToString(*this);
 }
-void Value::print(llvm::raw_ostream &Out) const {
+// FIXME: We do not support the multiple inheritance case where one of the base
+// classes has a pretty-printer and the other does not.
+void Value::print(llvm::raw_ostream &Out) {
   assert(OpaqueType != nullptr && "Can't print default Value");
-  Out << "Not implement yet.\n";
+
+  // Don't even try to print a void or an invalid type, it doesn't make sense.
+  if (getType()->isVoidType() || !isValid())
+    return;
+
+  // We need to get all the results together then print it, since `printType` is
+  // much faster than `printData`.
+  std::string Str;
+  llvm::raw_string_ostream SS(Str);
+
+  SS << "(";
+  printType(SS);
+  SS << ") ";
+  printData(SS);
+  SS << "\n";
+  Out << Str;
 }
 
 } // namespace clang
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index bdb3fc051d0b35..56b1141f02a463 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -583,7 +583,8 @@ StmtResult Parser::ParseExprStatement(ParsedStmtContext StmtCtx) {
   }
 
   Token *CurTok = nullptr;
-  // Note we shouldn't eat the token since the callback needs it.
+  // If the semicolon is missing at the end of REPL input, we want to print
+  // the result. Note we shouldn't eat the token since the callback needs it.
   if (Tok.is(tok::annot_repl_input_end))
     CurTok = &Tok;
   else
diff --git a/clang/test/Interpreter/pretty-print.c b/clang/test/Interpreter/pretty-print.c
index d21749a649e1c0..927cd836ab693b 100644
--- a/clang/test/Interpreter/pretty-print.c
+++ b/clang/test/Interpreter/pretty-print.c
@@ -3,9 +3,73 @@
 // RUN: cat %s | clang-repl -Xcc -xc  | FileCheck %s
 // RUN: cat %s | clang-repl -Xcc -std=c++11 | FileCheck %s
 
-// Fails with `Symbols not found: [ __clang_Interpreter_SetValueNoAlloc ]`.
 // UNSUPPORTED: hwasan
 
 const char* c_str = "Hello, world!"; c_str
 
-// CHECK: Not implement yet.
+char c = 'a'; c
+// CHECK: (char) 'a'
+
+c_str = "Goodbye, world!"; c_str
+// CHECK-NEXT: (const char *) "Goodbye, world!"
+
+const char* c_null_str = 0; c_null_str
+// CHECK-NEXT: (const char *) nullptr
+
+"Hello, world"
+// CHECK-NEXT: (const char[13]) "Hello, world"
+
+int x = 42; x
+// CHECK-NEXT: (int) 42
+
+&x
+// CHECK-NEXT: (int *) @0x{{[0-9a-f]+}}
+
+x - 2
+// CHECK-NEXT: (int) 40
+
+float f = 4.2f; f
+// CHECK-NEXT: (float) 4.20000f
+
+double d = 4.21; d
+// CHECK-NEXT: (double) 4.21000000000
+
+struct S1{} s1; s1
+// CHECK-NEXT: (S1 &) @0x{{[0-9a-f]+}}
+
+S1{}
+// CHECK-NEXT: (S1) @0x{{[0-9a-f]+}}
+
+struct S2 {int d;} E = {22}; E
+// CHECK-NEXT: (struct S2 &) @0x{{[0-9a-f]+}}
+E.d
+// CHECK-NEXT: (int) 22
+
+// Arrays.
+
+int arr[3] = {1,2,3}; arr
+// CHECK-NEXT: (int[3]) { 1, 2, 3 }
+
+int foo() { return 42; } foo()
+// CHECK-NEXT: (int) 42
+
+void bar() {} bar()
+
+struct ConstLiteral{};
+const char * caas__runtime__PrintValueRuntime(const struct ConstLiteral *) { \
+  return "ConstLiteral";                                                \
+}
+struct ConstLiteral CL; CL
+// CHECK-NEXT: ConstLiteral
+
+struct Point{int x; int y};
+const char * caas__runtime__PrintValueRuntime(const struct Point *p) { \
+  char[11 + 11 + 4 + 1] result;                                        \
+  sprintf(result, "(%d, %d)", p->x, p->y);                             \
+  return strdup(result);                                               \
+}
+
+Point P {1,2}; P
+// CHECK-NEXT: (1, 2)
+
+%quit
diff --git a/clang/test/Interpreter/pretty-print.cpp b/clang/test/Interpreter/pretty-print.cpp
new file mode 100644
index 00000000000000..b7a6c07c481dd3
--- /dev/null
+++ b/clang/test/Interpreter/pretty-print.cpp
@@ -0,0 +1,170 @@
+// RUN: clang-repl "int i = 10;" 'extern "C" int printf(const char*,...);' \
+// RUN:            'auto r1 = printf("i = %d\n", i);' | FileCheck --check-prefix=CHECK-DRIVER %s
+// UNSUPPORTED: system-aix
+// CHECK-DRIVER: i = 10
+// RUN: cat %s | clang-repl -Xcc -std=c++11 -Xcc -fno-delayed-template-parsing | FileCheck %s
+extern "C" int printf(const char*,...);
+
+struct NonPOD {        \
+  static int sI;       \
+  int I;               \
+  NonPOD(): I(sI++) {} \
+};
+namespace caas { namespace runtime {                            \
+  const char* PrintValueRuntime(const NonPOD* type) {           \
+  switch (type->I) {                                            \
+  default: return "out-of-bounds";                              \
+  case 0: return "0"; case 1: return "1"; case 2: return "2";   \
+  case 3: return "3"; case 4: return "4"; case 5: return "5";   \
+  }                                                             \
+}}}
+
+int NonPOD::sI = 0;
+
+NonPOD non_pod_arr[2][3];
+// Check array order after the value printing transformation. Also make sure we
+// can handle the forward declaration of operator new with placement.
+non_pod_arr
+// CHECK: (NonPOD[2][3]) { { 0, 1, 2 }, { 3, 4, 5 } }
+
+struct S3 { int* p; S3() { p = new int(42); } ~S3() { delete p; } };
+S3{}
+// CHECK-NEXT: (S3) @0x{{[0-9a-f]+}}
+S3 s3;
+s3
+// CHECK-NEXT: (S3 &) @0x{{[0-9a-f]+}}
+
+struct S4 { ~S4() { printf("~S4()\n"); }};
+S4{}
+// CHECK-NEXT: (S4) @0x{{[0-9a-f]+}}
+
+enum Enum{ e1 = -12, e2, e3=33, e4, e5 = 33};
+e2
+// CHECK-NEXT: (Enum) (e2) : int -11
+::e1
+// CHECK-NEXT: (Enum) (e1) : int -12
+
+enum class Color { Black = 0, Red, Green };
+Color::Black
+// CHECK-NEXT: (Color) (Color::Black) : int 0
+
+
+// Lambdas.
+
+auto Lambda1 = []{};
+Lambda1
+// CHECK-NEXT: ((lambda) &) @0x{{[0-9a-f]+}}
+[]{}
+// CHECK-NEXT: ((lambda at input_line_{{[0-9]+}}:1:1)) @0x{{[0-9a-f]+}}
+
+template<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
+
+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 MyDate {  \
+  unsigned year; \
+  char month;    \
+  char day;      \
+};
+
+#include <string>
+
+namespace caas { namespace runtime {                                    \
+    std::string PrintValueRuntime(const MyDate* d) {                    \
+      std::string result;                                               \
+      result = std::to_string(d->year) + '-';                           \
+      switch(d->month) { default: result += "invalid month"; break;     \
+      case 1: result += "Jan"; break; case 2: result += "Feb"; break;   \
+      case 3: result += "Mar"; break; case 4: result += "Apr"; break;   \
+      case 5: result += "May"; break; case 6: result += "Jun"; break;   \
+      case 7: result += "Jul"; break; case 8: result += "Aug"; break;   \
+      case 9: result += "Sep"; break; case 10: result += "Oct"; break;  \
+      case 11: result += "Nov"; break; case 12: result += "Dec"; break; \
+      }                                                                 \
+      result += '-';                                                    \
+      result += std::to_string(d->day);                                 \
+      return result;                                                    \
+    }                                                                   \
+  }}
+MyDate{2024, 8, 23}
+// CHECK-NEXT: (MyType) My pretty printer!
+%quit
+
diff --git a/clang/tools/clang-repl/CMakeLists.txt b/clang/tools/clang-repl/CMakeLists.txt
index a35ff13494e115..0b09bbc74e5818 100644
--- a/clang/tools/clang-repl/CMakeLists.txt
+++ b/clang/tools/clang-repl/CMakeLists.txt
@@ -33,6 +33,14 @@ if(MSVC)
       ??_U at YAPEAX_K@Z
       ??_V at YAXPEAX@Z
       ??3 at YAXPEAX_K@Z
+      ??6?$basic_ostream at DU?$char_traits at D@std@@@std@@QEAAAEAV01 at H@Z
+      ??6?$basic_ostream at DU?$char_traits at D@std@@@std@@QEAAAEAV01 at M@Z
+      ??6?$basic_ostream at DU?$char_traits at D@std@@@std@@QEAAAEAV01 at N@Z
+      ??6?$basic_ostream at DU?$char_traits at D@std@@@std@@QEAAAEAV01 at PEBX@Z
+      ??6?$basic_ostream at DU?$char_traits at D@std@@@std@@QEAAAEAV01 at P6AAEAV01@AEAV01@@Z at Z
+      ??$?6U?$char_traits at D@std@@@std@@YAAEAV?$basic_ostream at DU?$char_traits at D@std@@@0 at AEAV10@D at Z
+      ??$?6U?$char_traits at D@std@@@std@@YAAEAV?$basic_ostream at DU?$char_traits at D@std@@@0 at AEAV10@PEBD at Z
+      ?_Facet_Register at std@@YAXPEAV_Facet_base at 1@@Z
     )
   else()
     set(clang_repl_exports ${clang_repl_exports}
@@ -42,6 +50,14 @@ if(MSVC)
       ??_U at YAPAXI@Z
       ??_V at YAXPAX@Z
       ??_V at YAXPAXI@Z
+      ??6?$basic_ostream at DU?$char_traits at D@std@@@std@@QAEAAV01 at H@Z
+      ??6?$basic_ostream at DU?$char_traits at D@std@@@std@@QAEAAV01 at M@Z
+      ??6?$basic_ostream at DU?$char_traits at D@std@@@std@@QAEAAV01 at N@Z
+      ??6?$basic_ostream at DU?$char_traits at D@std@@@std@@QAEAAV01 at PBX@Z
+      ??6?$basic_ostream at DU?$char_traits at D@std@@@std@@QAEAAV01 at P6AAAV01@AAV01@@Z at Z
+      ??$?6U?$char_traits at D@std@@@std@@YAAAV?$basic_ostream at DU?$char_traits at D@std@@@0 at AAV10@D at Z
+      ??$?6U?$char_traits at D@std@@@std@@YAAAV?$basic_ostream at DU?$char_traits at D@std@@@0 at AAV10@PBD at Z
+      ?_Facet_Register at std@@YAXPAV_Facet_base at 1@@Z
     )
   endif()
 
diff --git a/clang/tools/clang-repl/ClangRepl.cpp b/clang/tools/clang-repl/ClangRepl.cpp
index 9cfc70462893dd..f4efb5e1b49eec 100644
--- a/clang/tools/clang-repl/ClangRepl.cpp
+++ b/clang/tools/clang-repl/ClangRepl.cpp
@@ -201,7 +201,6 @@ int main(int argc, const char **argv) {
     DeviceCI->LoadRequestedPlugins();
 
   std::unique_ptr<clang::Interpreter> Interp;
-
   if (CudaEnabled) {
     Interp = ExitOnErr(
         clang::Interpreter::createWithCUDA(std::move(CI), std::move(DeviceCI)));
diff --git a/clang/unittests/Interpreter/CodeCompletionTest.cpp b/clang/unittests/Interpreter/CodeCompletionTest.cpp
index 72fcce76a1029d..23cfc469695d28 100644
--- a/clang/unittests/Interpreter/CodeCompletionTest.cpp
+++ b/clang/unittests/Interpreter/CodeCompletionTest.cpp
@@ -26,7 +26,7 @@ auto CB = clang::IncrementalCompilerBuilder();
 
 class CodeCompletionTest : public InterpreterTestBase {
 public:
-  std::unique_ptr<Interpreter> Interp;
+  std::unique_ptr<clang::Interpreter> Interp;
 
   void SetUp() override {
     if (!HostSupportsJIT())
diff --git a/clang/unittests/Interpreter/InterpreterExtensionsTest.cpp b/clang/unittests/Interpreter/InterpreterExtensionsTest.cpp
index 5f1f29cebab148..85993e3e73e2c2 100644
--- a/clang/unittests/Interpreter/InterpreterExtensionsTest.cpp
+++ b/clang/unittests/Interpreter/InterpreterExtensionsTest.cpp
@@ -65,41 +65,13 @@ class InterpreterExtensionsTest : public InterpreterTestBase {
   }
 };
 
-class RecordRuntimeIBMetrics : public Interpreter {
-  struct NoopRuntimeInterfaceBuilder : public RuntimeInterfaceBuilder {
-    NoopRuntimeInterfaceBuilder(Sema &S) : S(S) {}
-
-    TransformExprFunction *getPrintValueTransformer() override {
-      TransformerQueries += 1;
-      return &noop;
-    }
-
-    static ExprResult noop(RuntimeInterfaceBuilder *Builder, Expr *E,
-                           ArrayRef<Expr *> FixedArgs) {
-      auto *B = static_cast<NoopRuntimeInterfaceBuilder *>(Builder);
-      B->TransformedExprs += 1;
-      return B->S.ActOnFinishFullExpr(E, /*DiscardedValue=*/false);
-    }
-
-    Sema &S;
-    size_t TransformedExprs = 0;
-    size_t TransformerQueries = 0;
-  };
-
-public:
-  // Inherit with using wouldn't make it public
-  RecordRuntimeIBMetrics(std::unique_ptr<CompilerInstance> CI, llvm::Error &Err)
-      : Interpreter(std::move(CI), Err) {}
-
-  std::unique_ptr<RuntimeInterfaceBuilder> FindRuntimeInterface() override {
-    assert(RuntimeIBPtr == nullptr && "We create the builder only once");
-    Sema &S = getCompilerInstance()->getSema();
-    auto RuntimeIB = std::make_unique<NoopRuntimeInterfaceBuilder>(S);
-    RuntimeIBPtr = RuntimeIB.get();
-    return RuntimeIB;
-  }
-
-  NoopRuntimeInterfaceBuilder *RuntimeIBPtr = nullptr;
+struct OutOfProcInterpreter : public Interpreter {
+  OutOfProcInterpreter(
+      std::unique_ptr<CompilerInstance> CI, llvm::Error &ErrOut,
+      std::unique_ptr<clang::ASTConsumer> Consumer,
+      std::unique_ptr<llvm::orc::LLJITBuilder> JITBuilder = nullptr)
+      : Interpreter(std::move(CI), ErrOut, std::move(JITBuilder),
+                    std::move(Consumer)) {}
 };
 
 TEST_F(InterpreterExtensionsTest, FindRuntimeInterface) {
@@ -108,13 +80,23 @@ TEST_F(InterpreterExtensionsTest, FindRuntimeInterface) {
 
   clang::IncrementalCompilerBuilder CB;
   llvm::Error ErrOut = llvm::Error::success();
-  RecordRuntimeIBMetrics Interp(cantFail(CB.CreateCpp()), ErrOut);
+  auto CI = cantFail(CB.CreateCpp());
+  // Do not attach the default consumer which is specialized for in-process.
+  class NoopConsumer : public ASTConsumer {};
+  std::unique_ptr<ASTConsumer> C = std::make_unique<NoopConsumer>();
+  OutOfProcInterpreter I(std::move(CI), ErrOut, std::move(C),
+                         /*JITBuilder=*/nullptr);
   cantFail(std::move(ErrOut));
-  cantFail(Interp.Parse("int a = 1; a"));
-  cantFail(Interp.Parse("int b = 2; b"));
-  cantFail(Interp.Parse("int c = 3; c"));
-  EXPECT_EQ(3U, Interp.RuntimeIBPtr->TransformedExprs);
-  EXPECT_EQ(1U, Interp.RuntimeIBPtr->TransformerQueries);
+  cantFail(I.Parse("int a = 1; a"));
+  cantFail(I.Parse("int b = 2; b"));
+  cantFail(I.Parse("int c = 3; c"));
+
+  // Make sure no clang::Value logic is attached by the Interpreter.
+  Value V1;
+  llvm::cantFail(I.ParseAndExecute("int x = 42;"));
+  llvm::cantFail(I.ParseAndExecute("x", &V1));
+  EXPECT_FALSE(V1.isValid());
+  EXPECT_FALSE(V1.hasValue());
 }
 
 class CustomJBInterpreter : public Interpreter {
@@ -139,7 +121,7 @@ TEST_F(InterpreterExtensionsTest, DefaultCrossJIT) {
   if (!IsARMTargetRegistered())
     GTEST_SKIP();
 
-  IncrementalCompilerBuilder CB;
+  clang::IncrementalCompilerBuilder CB;
   CB.SetTargetTriple("armv6-none-eabi");
   auto CI = cantFail(CB.CreateCpp());
   llvm::Error ErrOut = llvm::Error::success();
@@ -153,7 +135,7 @@ TEST_F(InterpreterExtensionsTest, CustomCrossJIT) {
 
   std::string TargetTriple = "armv6-none-eabi";
 
-  IncrementalCompilerBuilder CB;
+  clang::IncrementalCompilerBuilder CB;
   CB.SetTargetTriple(TargetTriple);
   auto CI = cantFail(CB.CreateCpp());
 

>From 24d0867459582939915b4a8175bcc2f2f2aede33 Mon Sep 17 00:00:00 2001
From: Vassil Vassilev <v.g.vassilev at gmail.com>
Date: Sat, 31 Aug 2024 13:21:26 +0000
Subject: [PATCH 2/3] store

---
 clang/include/clang/Interpreter/Interpreter.h |   2 +-
 .../__clang_interpreter_runtime_printvalue.h  |  28 +--
 clang/lib/Interpreter/IncrementalExecutor.cpp |   2 +-
 clang/lib/Interpreter/Interpreter.cpp         |   2 +-
 .../Interpreter/InterpreterValuePrinter.cpp   | 205 ++++++++++++------
 clang/test/Interpreter/pretty-print.c         |   5 +-
 clang/test/Interpreter/pretty-print.cpp       |   2 +-
 7 files changed, 158 insertions(+), 88 deletions(-)

diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h
index 195545a5b393b0..f394c8bf21e633 100644
--- a/clang/include/clang/Interpreter/Interpreter.h
+++ b/clang/include/clang/Interpreter/Interpreter.h
@@ -187,7 +187,7 @@ class Interpreter {
   std::string ValueDataToString(const Value &V);
   std::string ValueTypeToString(const Value &V) const;
 
-  llvm::Expected<Expr *> AttachValuePrinting(Expr *E);
+  llvm::Expected<Expr *> convertExprToValue(Expr *E);
 
   // When we deallocate clang::Value we need to run the destructor of the type.
   // This function forces emission of the needed dtor.
diff --git a/clang/lib/Headers/__clang_interpreter_runtime_printvalue.h b/clang/lib/Headers/__clang_interpreter_runtime_printvalue.h
index 0a1367f970d0fb..38c20a5390387f 100644
--- a/clang/lib/Headers/__clang_interpreter_runtime_printvalue.h
+++ b/clang/lib/Headers/__clang_interpreter_runtime_printvalue.h
@@ -60,20 +60,20 @@ __DECL_PRINT_VALUE_RUNTIME(void);        //
 __DECL_PRINT_VALUE_RUNTIME(void *);      //
 __DECL_PRINT_VALUE_RUNTIME(char *const); //
 __DECL_PRINT_VALUE_RUNTIME(char *);      //
-__DECL_PRINT_VALUE_RUNTIME(bool);
-__DECL_PRINT_VALUE_RUNTIME(char);
-__DECL_PRINT_VALUE_RUNTIME(signed char);
-__DECL_PRINT_VALUE_RUNTIME(short);
-__DECL_PRINT_VALUE_RUNTIME(unsigned short);
-__DECL_PRINT_VALUE_RUNTIME(int);
-__DECL_PRINT_VALUE_RUNTIME(unsigned int);
-__DECL_PRINT_VALUE_RUNTIME(long);
-__DECL_PRINT_VALUE_RUNTIME(unsigned long);
-__DECL_PRINT_VALUE_RUNTIME(long long);
-__DECL_PRINT_VALUE_RUNTIME(unsigned long long);
-__DECL_PRINT_VALUE_RUNTIME(float);
-__DECL_PRINT_VALUE_RUNTIME(double);
-__DECL_PRINT_VALUE_RUNTIME(long double);
+// __DECL_PRINT_VALUE_RUNTIME(bool);
+// __DECL_PRINT_VALUE_RUNTIME(char);
+// __DECL_PRINT_VALUE_RUNTIME(signed char);
+// __DECL_PRINT_VALUE_RUNTIME(short);
+// __DECL_PRINT_VALUE_RUNTIME(unsigned short);
+// __DECL_PRINT_VALUE_RUNTIME(int);
+// __DECL_PRINT_VALUE_RUNTIME(unsigned int);
+// __DECL_PRINT_VALUE_RUNTIME(long);
+// __DECL_PRINT_VALUE_RUNTIME(unsigned long);
+// __DECL_PRINT_VALUE_RUNTIME(long long);
+// __DECL_PRINT_VALUE_RUNTIME(unsigned long long);
+// __DECL_PRINT_VALUE_RUNTIME(float);
+// __DECL_PRINT_VALUE_RUNTIME(double);
+// __DECL_PRINT_VALUE_RUNTIME(long double);
 #endif
 
 namespace __repl_runtime_detail {
diff --git a/clang/lib/Interpreter/IncrementalExecutor.cpp b/clang/lib/Interpreter/IncrementalExecutor.cpp
index 4d2adecaafce74..1824a5b4570a93 100644
--- a/clang/lib/Interpreter/IncrementalExecutor.cpp
+++ b/clang/lib/Interpreter/IncrementalExecutor.cpp
@@ -118,4 +118,4 @@ IncrementalExecutor::getSymbolAddress(llvm::StringRef Name,
   return SymOrErr->getAddress();
 }
 
-} // namespace clang
+} // end namespace clang
diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp
index 039ee4ecc99dd8..b4cf63f00e0bee 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -262,7 +262,7 @@ class InProcessPrintingASTConsumer final : public MultiplexConsumer {
       if (auto *TLSD = llvm::dyn_cast<TopLevelStmtDecl>(D))
         if (TLSD && TLSD->isSemiMissing()) {
           auto ExprOrErr =
-              Interp.AttachValuePrinting(cast<Expr>(TLSD->getStmt()));
+              Interp.convertExprToValue(cast<Expr>(TLSD->getStmt()));
           if (llvm::Error E = ExprOrErr.takeError()) {
             llvm::logAllUnhandledErrors(std::move(E), llvm::errs(),
                                         "Value printing failed: ");
diff --git a/clang/lib/Interpreter/InterpreterValuePrinter.cpp b/clang/lib/Interpreter/InterpreterValuePrinter.cpp
index a622d681a96f84..e9269c4452da92 100644
--- a/clang/lib/Interpreter/InterpreterValuePrinter.cpp
+++ b/clang/lib/Interpreter/InterpreterValuePrinter.cpp
@@ -14,6 +14,7 @@
 #include "InterpreterUtils.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/PrettyPrinter.h"
+#include "clang/AST/QualTypeNames.h"
 #include "clang/AST/Type.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Interpreter/Interpreter.h"
@@ -26,8 +27,11 @@
 #include "llvm/Support/raw_ostream.h"
 
 #include <cassert>
+#include <cstdarg>
 #include <string>
 
+#define DEBUG_TYPE "interp-value"
+
 using namespace clang;
 
 static std::string PrintDeclType(const QualType &QT, NamedDecl *D) {
@@ -202,20 +206,11 @@ static std::string PrintString(const char *const *Ptr, size_t N = 10000) {
 }
 
 // Build the CallExpr to `PrintValueRuntime`.
-static void BuildWrapperBody(Interpreter &Interp, Sema &S, ASTContext &Ctx,
-                             FunctionDecl *WrapperFD, QualType QT,
-                             const void *ValPtr) {
+static llvm::Error BuildWrapperBody(LookupResult &R, Sema &S,
+                                    ASTContext &Ctx, FunctionDecl *WrapperFD,
+                                    QualType QT, const void *ValPtr) {
   Sema::SynthesizedFunctionScope SemaFScope(S, WrapperFD);
   clang::DeclarationName RuntimeCallName;
-  if (Ctx.getLangOpts().CPlusPlus)
-    RuntimeCallName = S.PP.getIdentifierInfo("PrintValueRuntime");
-  else
-    RuntimeCallName =
-        S.PP.getIdentifierInfo("caas__runtime__PrintValueRuntime");
-
-  clang::LookupResult R(S, RuntimeCallName, SourceLocation(),
-                        clang::Sema::LookupOrdinaryName);
-  S.LookupName(R, S.getCurScope());
 
   Expr *OverldExpr = UnresolvedLookupExpr::Create(
       Ctx, /*NamingClass=*/nullptr, NestedNameSpecifierLoc(),
@@ -254,22 +249,33 @@ static void BuildWrapperBody(Interpreter &Interp, Sema &S, ASTContext &Ctx,
   ExprResult RuntimeCall =
       S.ActOnCallExpr(S.getCurScope(), OverldExpr, SourceLocation(), CallArgs,
                       SourceLocation());
-  assert(!RuntimeCall.isInvalid() && "Cannot create call to PrintValueRuntime");
+  if (RuntimeCall.isInvalid()) {
+    std::string Name = R.getLookupName().getAsString();
+    return llvm::make_error<llvm::StringError>(
+                                               "Cannot create call to " + Name, llvm::inconvertibleErrorCode());
+  }
 
   // Create the ReturnStmt.
   StmtResult RetStmt =
       S.ActOnReturnStmt(SourceLocation(), RuntimeCall.get(), S.getCurScope());
-  assert(!RetStmt.isInvalid() && "Cannot create ReturnStmt");
+
+  if (RetStmt.isInvalid())
+    return llvm::make_error<llvm::StringError>("Cannot create a return stmt",
+                                               llvm::inconvertibleErrorCode());
 
   // Create the CompoundStmt.
   StmtResult Body =
       CompoundStmt::Create(Ctx, {RetStmt.get()}, FPOptionsOverride(),
                            SourceLocation(), SourceLocation());
-  assert(!Body.isInvalid() && "Cannot create function body");
+  if (Body.isInvalid())
+    return llvm::make_error<llvm::StringError>("Cannot create function body",
+                                               llvm::inconvertibleErrorCode());
 
   WrapperFD->setBody(Body.get());
   // Add attribute `__attribute__((used))`.
   WrapperFD->addAttr(UsedAttr::CreateImplicit(Ctx));
+
+  return llvm::Error::success();
 }
 
 static constexpr const char *const WrapperName = "__InterpreterCallPrint";
@@ -423,21 +429,42 @@ REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const char **Val) {
   return PrintString(Val);
 }
 
+// Check if the user has implemented a function that avoids fallback to
+// defaults.
+static clang::LookupResult LookupUserDefined(Sema &S, QualType QT) {
+  PrintingPolicy Policy = S.getASTContext().getPrintingPolicy();
+  Policy.SuppressElaboration = 1;
+  Policy.SuppressTagKeyword = 1;
+  Policy.FullyQualifiedName = 1;
+  std::string TypeStr = QT.getAsString(Policy);
+  DeclarationName Name = S.PP.getIdentifierInfo("caas_runtime_to_string_" + TypeStr);
+
+  LLVM_DEBUG(llvm::dbgs() << "Looking for user-defined '" << Name <<"'");
+
+  LookupResult R(S, Name, SourceLocation(),Sema::LookupOrdinaryName);
+  S.LookupName(R, S.getCurScope());
+  return R;
+}
+
 namespace clang {
+
 std::string Interpreter::ValueDataToString(const Value &V) {
+  Sema &S = getCompilerInstance()->getSema();
+  ASTContext &Ctx = S.getASTContext();
+
   QualType QT = V.getType();
-  QualType DesugaredTy = QT.getDesugaredType(V.getASTContext());
+  QualType DesugaredTy = QT.getDesugaredType(Ctx);
   QualType NonRefTy = DesugaredTy.getNonReferenceType();
 
-  if (NonRefTy->isEnumeralType())
-    return PrintEnum(V);
+  if ((NonRefTy->isPointerType() || NonRefTy->isMemberPointerType()) &&
+      NonRefTy->getPointeeType()->isFunctionProtoType())
+    return PrintFunction(V, V.getPtr());
 
   if (NonRefTy->isFunctionType())
     return PrintFunction(V, &V);
 
-  if ((NonRefTy->isPointerType() || NonRefTy->isMemberPointerType()) &&
-      NonRefTy->getPointeeType()->isFunctionProtoType())
-    return PrintFunction(V, V.getPtr());
+  if (NonRefTy->isEnumeralType())
+    return PrintEnum(V);
 
   if (NonRefTy->isNullPtrType())
     return "nullptr\n";
@@ -446,46 +473,52 @@ std::string Interpreter::ValueDataToString(const Value &V) {
   if (auto *BT = DesugaredTy.getCanonicalType()->getAs<BuiltinType>()) {
     switch (BT->getKind()) {
     default:
-      return "{ unknown builtin type: }" + std::to_string(BT->getKind());
-#define X(type, name)                                                          \
-  case clang::BuiltinType::name: {                                             \
-    type val = V.get##name();                                                  \
-    return PrintValueRuntime(&val);                                            \
-  }
-      REPL_BUILTIN_TYPES
-#undef X
+      return "{ error: unknown builtin type '" + std::to_string(BT->getKind()) +" '}";
+    case clang::BuiltinType::Bool: { bool val = V.getBool(); return PrintValueRuntime(&val); } case clang::BuiltinType::Char_S: { char val = V.getChar_S(); return PrintValueRuntime(&val); } case clang::BuiltinType::SChar: { signed char val = V.getSChar(); return PrintValueRuntime(&val); } case clang::BuiltinType::Char_U: { unsigned char val = V.getChar_U(); return PrintValueRuntime(&val); } case clang::BuiltinType::UChar: { unsigned char val = V.getUChar(); return PrintValueRuntime(&val); } case clang::BuiltinType::Short: { short val = V.getShort(); return PrintValueRuntime(&val); } case clang::BuiltinType::UShort: { unsigned short val = V.getUShort(); return PrintValueRuntime(&val); } case clang::BuiltinType::Int: { int val = V.getInt(); return PrintValueRuntime(&val); } case clang::BuiltinType::UInt: { unsigned int val = V.getUInt(); return PrintValueRuntime(&val); } case clang::BuiltinType::Long: { long val = V.getLong(); return PrintValueRuntime(&val); } case clang::BuiltinType::ULong: { unsigned long val = V.getULong(); return PrintValueRuntime(&val); } case clang::BuiltinType::LongLong: { long long val = V.getLongLong(); return PrintValueRuntime(&val); } case clang::BuiltinType::ULongLong: { unsigned long long val = V.getULongLong(); return PrintValueRuntime(&val); } case clang::BuiltinType::Float: { float val = V.getFloat(); return PrintValueRuntime(&val); } case clang::BuiltinType::Double: { double val = V.getDouble(); return PrintValueRuntime(&val); } case clang::BuiltinType::LongDouble: { long double val = V.getLongDouble(); return PrintValueRuntime(&val); }
     }
   }
   if (auto *CXXRD = NonRefTy->getAsCXXRecordDecl())
     if (CXXRD->isLambda())
       return PrintAddress(V.getPtr(), '@');
 
-  // All fails then generate a runtime call, this is slow.
-  Sema &S = getCompilerInstance()->getSema();
-  ASTContext &Ctx = S.getASTContext();
+  // FIXME: Add support for custom printers in C.
+  if (NonRefTy->isPointerType()) {
+    if (NonRefTy->getPointeeType()->isCharType())
+      return PrintValueRuntime((const char**)V.getPtrAddress());
+    return PrintValueRuntime(V.getPtr());
+  }
 
-  QualType RetTy;
-  if (Ctx.getLangOpts().CPlusPlus && !StdString) {
+  QualType RetTy = Ctx.getPointerType(Ctx.CharTy.withConst());
 
-    // Only include the header on demand because it's very heavy.
-    if (llvm::Error E = ParseAndExecute(
-            "#include <__clang_interpreter_runtime_printvalue.h>")) {
-      llvm::logAllUnhandledErrors(std::move(E), llvm::errs(), "Parsing failed");
-      return "{Internal error}";
-    }
+  LookupResult R = LookupUserDefined(S, QT);
+  if (!R.isSingleResult())
+    return "{error: multiple results for '" + R.getLookupName().getAsString() + "'}";
 
-    // Find and cache std::string.
-    NamespaceDecl *Std = LookupNamespace(S, "std");
-    assert(Std && "Cannot find namespace std");
-    Decl *StdStringDecl = LookupNamed(S, "string", Std);
-    assert(StdStringDecl && "Cannot find std::string");
-    const auto *StdStringTyDecl = llvm::dyn_cast<TypeDecl>(StdStringDecl);
-    assert(StdStringTyDecl && "Cannot find type of std::string");
-    RetTy = QualType(StdStringTyDecl->getTypeForDecl(), /*Quals=*/0);
-  } else {
-    RetTy = Ctx.getPointerType(Ctx.CharTy.withConst());
+  if (R.empty()) {
+
+    if (!Ctx.getLangOpts().CPlusPlus)
+      return PrintValueRuntime(V.getPtr());
+
+    if (S.StdNamespace && !StdString) {
+
+      // Only include the header on demand because it's very heavy.
+      auto TUorE = Parse("#include <__clang_interpreter_runtime_printvalue.h>");
+      if (llvm::Error E = TUorE.takeError() ) {
+        llvm::logAllUnhandledErrors(std::move(E), llvm::errs(), "Parsing failed");
+        return "{error: cannot parse system header}";
+      }
+
+      // Find and cache std::string.
+      Decl *StdStringDecl = LookupNamed(S, "string", S.getStdNamespace());
+      assert(StdStringDecl && "Cannot find std::string");
+      const auto *StdStringTyDecl = llvm::dyn_cast<TypeDecl>(StdStringDecl);
+      assert(StdStringTyDecl && "Cannot find type of std::string");
+      RetTy = QualType(StdStringTyDecl->getTypeForDecl(), /*Quals=*/0);
+    }
   }
 
+  // All fails then generate a runtime call, this is slow.
+
   // Create the wrapper function.
   DeclarationName DeclName = &Ctx.Idents.get(CreateUniqName(WrapperName));
   QualType FnTy =
@@ -501,7 +534,30 @@ std::string Interpreter::ValueDataToString(const Value &V) {
   if (!V.isManuallyAlloc())
     ValPtr = V.getPtrAddress();
 
-  BuildWrapperBody(*this, S, Ctx, WrapperFD, V.getType(), ValPtr);
+  // // Check if the user has implemented a function that avoids fallback to
+  // // defaults.
+  // if (Ctx.getLangOpts().CPlusPlus) {
+  //   RuntimeCallName = S.PP.getIdentifierInfo("PrintValueRuntime");
+  // } else {
+  //   PrintingPolicy Policy = Ctx.getPrintingPolicy();
+  //   Policy.SuppressElaboration = 1;
+  //   Policy.SuppressTagKeyword = 1;
+  //   Policy.FullyQualifiedName = 1;
+  //   std::string TypeStr = QT.getAsString(Policy);
+  //   printf("KKK %s\n", TypeStr.c_str());
+  //   //      clang::TypeName::getFullyQualifiedName(QT, Ctx, Policy);
+  //   RuntimeCallName =
+  //     S.PP.getIdentifierInfo("caas__runtime__PrintValueRuntime__" + TypeStr);
+  // }
+
+  // LookupResult R(S, RuntimeCallName, SourceLocation(),Sema::LookupOrdinaryName);
+  // S.LookupName(R, S.getCurScope());
+
+  if (llvm::Error E = BuildWrapperBody(R, S, Ctx, WrapperFD, V.getType(), ValPtr)) {
+    llvm::logAllUnhandledErrors(std::move(E), llvm::errs(),
+                                "Fail to build a wrapper function");
+    return "{Unable to print the value!}";
+  }
 
   auto AddrOrErr = CompileDecl(*this, WrapperFD);
   if (!AddrOrErr)
@@ -509,7 +565,7 @@ std::string Interpreter::ValueDataToString(const Value &V) {
                                 "Fail to get symbol address");
   if (auto *Main = AddrOrErr->toPtr<std::string (*)()>())
     return (*Main)();
-  return "Unable to print the value!";
+  return "{Unable to print the value!}";
 }
 
 std::string Interpreter::ValueTypeToString(const Value &V) const {
@@ -564,7 +620,12 @@ class InterfaceKindVisitor
   }
 
   InterfaceKind VisitRecordType(const RecordType *Ty) {
-    return InterfaceKind::WithAlloc;
+    if (S.getLangOpts().CPlusPlus)
+      return InterfaceKind::WithAlloc;
+    ExprResult AddrOfE = S.CreateBuiltinUnaryOp(SourceLocation(), UO_AddrOf, E->IgnoreImpCasts());
+    assert(!AddrOfE.isInvalid() && "Can not create unary expression");
+    Args.push_back(AddrOfE.get());
+    return InterfaceKind::NoAlloc;
   }
 
   InterfaceKind VisitMemberPointerType(const MemberPointerType *Ty) {
@@ -635,9 +696,14 @@ class InterfaceKindVisitor
   }
 };
 
+static constexpr llvm::StringRef VPName[] = {
+  "__clang_Interpreter_SetValueNoAlloc",
+  "__clang_Interpreter_SetValueWithAlloc",
+  "__clang_Interpreter_SetValueCopyArr", "__ci_newtag"};
+
 // This synthesizes a call expression to a speciall
 // function that is responsible for generating the Value.
-// In general, we transform:
+// In general, we transform c++:
 //   clang-repl> x
 // To:
 //   // 1. If x is a built-in type like int, float.
@@ -648,7 +714,7 @@ class InterfaceKindVisitor
 //   // 3. If x is a struct, but a rvalue.
 //   new (__clang_Interpreter_SetValueWithAlloc(ThisInterp, OpaqueValue,
 //   xQualType)) (x);
-llvm::Expected<Expr *> Interpreter::AttachValuePrinting(Expr *E) {
+llvm::Expected<Expr *> Interpreter::convertExprToValue(Expr *E) {
   Sema &S = getCompilerInstance()->getSema();
   ASTContext &Ctx = S.getASTContext();
 
@@ -670,23 +736,19 @@ llvm::Expected<Expr *> Interpreter::AttachValuePrinting(Expr *E) {
       Interface = S.BuildDeclarationNameExpr(CSS, R, /*ADL=*/false).get();
       return llvm::Error::success();
     };
-    static constexpr llvm::StringRef Builtin[] = {
-        "__clang_Interpreter_SetValueNoAlloc",
-        "__clang_Interpreter_SetValueWithAlloc",
-        "__clang_Interpreter_SetValueCopyArr", "__ci_newtag"};
     if (llvm::Error Err =
-            LookupInterface(ValuePrintingInfo[NoAlloc], Builtin[NoAlloc]))
+            LookupInterface(ValuePrintingInfo[NoAlloc], VPName[NoAlloc]))
       return std::move(Err);
 
     if (Ctx.getLangOpts().CPlusPlus) {
       if (llvm::Error Err =
-              LookupInterface(ValuePrintingInfo[WithAlloc], Builtin[WithAlloc]))
+              LookupInterface(ValuePrintingInfo[WithAlloc], VPName[WithAlloc]))
         return std::move(Err);
       if (llvm::Error Err =
-              LookupInterface(ValuePrintingInfo[CopyArray], Builtin[CopyArray]))
+              LookupInterface(ValuePrintingInfo[CopyArray], VPName[CopyArray]))
         return std::move(Err);
       if (llvm::Error Err =
-              LookupInterface(ValuePrintingInfo[NewTag], Builtin[NewTag]))
+              LookupInterface(ValuePrintingInfo[NewTag], VPName[NewTag]))
         return std::move(Err);
     }
   }
@@ -725,6 +787,8 @@ llvm::Expected<Expr *> Interpreter::AttachValuePrinting(Expr *E) {
   Scope *Scope = nullptr;
   ExprResult SetValueE;
   InterfaceKind Kind = V.computeInterfaceKind(DesugaredTy);
+  if (!Ctx.getLangOpts().CPlusPlus && Kind == InterfaceKind::WithAlloc)
+    Kind = InterfaceKind::NoAlloc;
   switch (Kind) {
   case InterfaceKind::WithAlloc:
     LLVM_FALLTHROUGH;
@@ -733,7 +797,9 @@ llvm::Expected<Expr *> Interpreter::AttachValuePrinting(Expr *E) {
     ExprResult AllocCall =
         S.ActOnCallExpr(Scope, ValuePrintingInfo[InterfaceKind::WithAlloc],
                         E->getBeginLoc(), AdjustedArgs, E->getEndLoc());
-    assert(!AllocCall.isInvalid() && "Can't create runtime interface call!");
+    if (AllocCall.isInvalid())
+      return llvm::make_error<llvm::StringError>("Cannot call to " + VPName[WithAlloc],
+                          llvm::inconvertibleErrorCode());
 
     TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ty, SourceLocation());
 
@@ -755,6 +821,11 @@ llvm::Expected<Expr *> Interpreter::AttachValuePrinting(Expr *E) {
       SetValueE =
           S.ActOnCallExpr(Scope, ValuePrintingInfo[InterfaceKind::CopyArray],
                           SourceLocation(), Args, SourceLocation());
+      if (SetValueE.isInvalid())
+        return llvm::make_error<llvm::StringError>("Cannot call to " + VPName[CopyArray],
+                                                   llvm::inconvertibleErrorCode());
+      break;
+      //return SetValueE.get();
     }
     Expr *Args[] = {AllocCall.get(), ValuePrintingInfo[InterfaceKind::NewTag]};
     ExprResult CXXNewCall = S.BuildCXXNew(
@@ -764,8 +835,10 @@ llvm::Expected<Expr *> Interpreter::AttachValuePrinting(Expr *E) {
         /*TypeIdParens=*/SourceRange(), TSI->getType(), TSI, std::nullopt,
         E->getSourceRange(), E);
 
-    assert(!CXXNewCall.isInvalid() &&
-           "Can't create runtime placement new call!");
+    if (CXXNewCall.isInvalid())
+      return llvm::make_error<llvm::StringError>(
+                          "Cannot build a call to placement new",
+                          llvm::inconvertibleErrorCode());
 
     SetValueE = S.ActOnFinishFullExpr(CXXNewCall.get(),
                                       /*DiscardedValue=*/false);
diff --git a/clang/test/Interpreter/pretty-print.c b/clang/test/Interpreter/pretty-print.c
index 927cd836ab693b..63e63631640a3a 100644
--- a/clang/test/Interpreter/pretty-print.c
+++ b/clang/test/Interpreter/pretty-print.c
@@ -37,9 +37,6 @@ double d = 4.21; d
 struct S1{} s1; s1
 // CHECK-NEXT: (S1 &) @0x{{[0-9a-f]+}}
 
-S1{}
-// CHECK-NEXT: (S1) @0x{{[0-9a-f]+}}
-
 struct S2 {int d;} E = {22}; E
 // CHECK-NEXT: (struct S2 &) @0x{{[0-9a-f]+}}
 E.d
@@ -62,7 +59,7 @@ const char * caas__runtime__PrintValueRuntime(const struct ConstLiteral *) { \
 struct ConstLiteral CL; CL
 // CHECK-NEXT: ConstLiteral
 
-struct Point{int x; int y};
+struct Point{int x; int y;};
 const char * caas__runtime__PrintValueRuntime(const struct Point *p) { \
   char[11 + 11 + 4 + 1] result;                                        \
   sprintf(result, "(%d, %d)", p->x, p->y);                             \
diff --git a/clang/test/Interpreter/pretty-print.cpp b/clang/test/Interpreter/pretty-print.cpp
index b7a6c07c481dd3..87f595fd03360b 100644
--- a/clang/test/Interpreter/pretty-print.cpp
+++ b/clang/test/Interpreter/pretty-print.cpp
@@ -165,6 +165,6 @@ namespace caas { namespace runtime {                                    \
     }                                                                   \
   }}
 MyDate{2024, 8, 23}
-// CHECK-NEXT: (MyType) My pretty printer!
+// CHECK-NEXT: (MyDate) 2024-Aug-23
 %quit
 

>From 9554375738fcfaebab36e41f57618dafc5d7264c Mon Sep 17 00:00:00 2001
From: Vassil Vassilev <v.g.vassilev at gmail.com>
Date: Fri, 6 Sep 2024 09:00:11 +0000
Subject: [PATCH 3/3] store

---
 clang/include/clang/Interpreter/Interpreter.h |   2 +
 clang/include/clang/Interpreter/Value.h       |   2 +-
 clang/lib/Interpreter/InterpreterUtils.h      |   2 +-
 .../Interpreter/InterpreterValuePrinter.cpp   | 511 ++++++++----------
 clang/test/Interpreter/pretty-print.cpp       |   8 +-
 clang/tools/clang-repl/ClangRepl.cpp          |   2 +-
 6 files changed, 239 insertions(+), 288 deletions(-)

diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h
index f394c8bf21e633..1763afd240a2c4 100644
--- a/clang/include/clang/Interpreter/Interpreter.h
+++ b/clang/include/clang/Interpreter/Interpreter.h
@@ -41,6 +41,7 @@ class CXXRecordDecl;
 class Decl;
 class IncrementalExecutor;
 class IncrementalParser;
+class LookupResult;
 
 /// Create a pre-configured \c CompilerInstance for incremental processing.
 class IncrementalCompilerBuilder {
@@ -186,6 +187,7 @@ class Interpreter {
 
   std::string ValueDataToString(const Value &V);
   std::string ValueTypeToString(const Value &V) const;
+  std::string CallUserSpecifiedPrinter(LookupResult &R, const Value &V);
 
   llvm::Expected<Expr *> convertExprToValue(Expr *E);
 
diff --git a/clang/include/clang/Interpreter/Value.h b/clang/include/clang/Interpreter/Value.h
index 5417c550e7f985..664f9e9008603e 100644
--- a/clang/include/clang/Interpreter/Value.h
+++ b/clang/include/clang/Interpreter/Value.h
@@ -178,7 +178,7 @@ class REPL_EXTERNAL_VISIBILITY Value {
   template <typename T> struct convertFwd {
     static T cast(const Value &V) {
       if (V.isPointerOrObjectType())
-        return (T)(uintptr_t)V.as<void *>();
+        return *(T*)(uintptr_t)V.as<void *>();
       if (!V.isValid() || V.isVoid()) {
         return T();
       }
diff --git a/clang/lib/Interpreter/InterpreterUtils.h b/clang/lib/Interpreter/InterpreterUtils.h
index 4b92cf0c4bf978..bd23f249551b5f 100644
--- a/clang/lib/Interpreter/InterpreterUtils.h
+++ b/clang/lib/Interpreter/InterpreterUtils.h
@@ -45,7 +45,7 @@ NamespaceDecl *LookupNamespace(Sema &S, llvm::StringRef Name,
                                const DeclContext *Within = nullptr);
 
 NamedDecl *LookupNamed(Sema &S, llvm::StringRef Name,
-                       const DeclContext *Within);
+                       const DeclContext *Within = nullptr);
 
 NestedNameSpecifier *CreateNestedNameSpecifier(const ASTContext &Ctx,
                                                const NamespaceDecl *Namesp);
diff --git a/clang/lib/Interpreter/InterpreterValuePrinter.cpp b/clang/lib/Interpreter/InterpreterValuePrinter.cpp
index e9269c4452da92..df2ee3edcc1563 100644
--- a/clang/lib/Interpreter/InterpreterValuePrinter.cpp
+++ b/clang/lib/Interpreter/InterpreterValuePrinter.cpp
@@ -28,13 +28,14 @@
 
 #include <cassert>
 #include <cstdarg>
+#include <sstream>
 #include <string>
 
 #define DEBUG_TYPE "interp-value"
 
 using namespace clang;
 
-static std::string PrintDeclType(const QualType &QT, NamedDecl *D) {
+static std::string DeclTypeToString(const QualType &QT, NamedDecl *D) {
   std::string Str;
   llvm::raw_string_ostream SS(Str);
   if (QT.hasQualifiers())
@@ -43,7 +44,7 @@ static std::string PrintDeclType(const QualType &QT, NamedDecl *D) {
   return Str;
 }
 
-static std::string PrintQualType(ASTContext &Ctx, QualType QT) {
+static std::string QualTypeToString(ASTContext &Ctx, QualType QT) {
   PrintingPolicy Policy(Ctx.getPrintingPolicy());
   // Print the Allocator in STL containers, for instance.
   Policy.SuppressDefaultTemplateArgs = false;
@@ -65,10 +66,10 @@ static std::string PrintQualType(ASTContext &Ctx, QualType QT) {
   const QualType NonRefTy = QT.getNonReferenceType();
 
   if (const auto *TTy = llvm::dyn_cast<TagType>(NonRefTy))
-    return PrintDeclType(NonRefTy, TTy->getDecl());
+    return DeclTypeToString(NonRefTy, TTy->getDecl());
 
   if (const auto *TRy = dyn_cast<RecordType>(NonRefTy))
-    return PrintDeclType(NonRefTy, TRy->getDecl());
+    return DeclTypeToString(NonRefTy, TRy->getDecl());
 
   const QualType Canon = NonRefTy.getCanonicalType();
 
@@ -90,12 +91,12 @@ static std::string PrintQualType(ASTContext &Ctx, QualType QT) {
       return GetFullTypeName(Ctx, Canon);
     else if (llvm::isa<TemplateSpecializationType>(SSDesugar))
       return GetFullTypeName(Ctx, NonRefTy);
-    return PrintDeclType(NonRefTy, TDTy->getDecl());
+    return DeclTypeToString(NonRefTy, TDTy->getDecl());
   }
   return GetFullTypeName(Ctx, NonRefTy);
 }
 
-static std::string PrintEnum(const Value &V) {
+static std::string EnumToString(const Value &V) {
   std::string Str;
   llvm::raw_string_ostream SS(Str);
   ASTContext &Ctx = const_cast<ASTContext &>(V.getASTContext());
@@ -119,11 +120,11 @@ static std::string PrintEnum(const Value &V) {
   }
   llvm::SmallString<64> APStr;
   AP.toString(APStr, /*Radix=*/10);
-  SS << " : " << PrintQualType(Ctx, ED->getIntegerType()) << " " << APStr;
+  SS << " : " << QualTypeToString(Ctx, ED->getIntegerType()) << " " << APStr;
   return Str;
 }
 
-static std::string PrintFunction(const Value &V, const void *Ptr) {
+static std::string FunctionToString(const Value &V, const void *Ptr) {
   std::string Str;
   llvm::raw_string_ostream SS(Str);
   SS << "Function @" << Ptr;
@@ -155,7 +156,7 @@ static std::string PrintFunction(const Value &V, const void *Ptr) {
   return Str;
 }
 
-static std::string PrintAddress(const void *Ptr, char Prefix) {
+static std::string AddressToString(const void *Ptr, char Prefix) {
   std::string Str;
   llvm::raw_string_ostream SS(Str);
   if (!Ptr)
@@ -164,51 +165,10 @@ static std::string PrintAddress(const void *Ptr, char Prefix) {
   return Str;
 }
 
-// FIXME: Encodings. Handle unprintable characters such as control characters.
-static std::string PrintOneChar(char Val) {
-  std::string Str;
-  llvm::raw_string_ostream SS(Str);
-
-  SS << "'" << Val << "'";
-  return Str;
-}
-
-// Char pointers
-// Assumption is this is a string.
-// N is limit to prevent endless loop if Ptr is not really a string.
-static std::string PrintString(const char *const *Ptr, size_t N = 10000) {
-  std::string Str;
-  llvm::raw_string_ostream SS(Str);
-
-  const char *Start = *Ptr;
-  if (!Start)
-    return "nullptr";
-
-  const char *End = Start + N;
-  // If we're gonna do this, better make sure the end is valid too
-  // FIXME: getpagesize() & GetSystemInfo().dwPageSize might be better
-  static constexpr auto PAGE_SIZE = 1024;
-  while (N > 1024) {
-    N -= PAGE_SIZE;
-    End = Start + N;
-  }
-
-  if (*Start == 0)
-    return "\"\"";
-
-  // Copy the bytes until we get a null-terminator
-  SS << "\"";
-  while (Start < End && *Start)
-    SS << *Start++;
-  SS << "\"";
-
-  return Str;
-}
-
 // Build the CallExpr to `PrintValueRuntime`.
-static llvm::Error BuildWrapperBody(LookupResult &R, Sema &S,
-                                    ASTContext &Ctx, FunctionDecl *WrapperFD,
-                                    QualType QT, const void *ValPtr) {
+static llvm::Error BuildWrapperBody(LookupResult &R, Sema &S, ASTContext &Ctx,
+                                    FunctionDecl *WrapperFD, QualType QT,
+                                    const void *ValPtr) {
   Sema::SynthesizedFunctionScope SemaFScope(S, WrapperFD);
   clang::DeclarationName RuntimeCallName;
 
@@ -251,8 +211,8 @@ static llvm::Error BuildWrapperBody(LookupResult &R, Sema &S,
                       SourceLocation());
   if (RuntimeCall.isInvalid()) {
     std::string Name = R.getLookupName().getAsString();
-    return llvm::make_error<llvm::StringError>(
-                                               "Cannot create call to " + Name, llvm::inconvertibleErrorCode());
+    return llvm::make_error<llvm::StringError>("Cannot create call to " + Name,
+                                               llvm::inconvertibleErrorCode());
   }
 
   // Create the ReturnStmt.
@@ -313,212 +273,81 @@ static std::string CreateUniqName(std::string Base) {
   return Base;
 }
 
-REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const void *Ptr) {
-  return PrintAddress(Ptr, '@');
-}
-
-REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const void **Ptr) {
-  return PrintAddress(*Ptr, '@');
-}
-
-REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const bool *Val) {
-  if (*Val)
-    return "true";
-  return "false";
-}
-
-REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const char *Val) {
-  return PrintOneChar(*Val);
-}
-
-REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const signed char *Val) {
-  return PrintOneChar(*Val);
-}
-
-REPL_EXTERNAL_VISIBILITY std::string
-PrintValueRuntime(const unsigned char *Val) {
-  return PrintOneChar(*Val);
-}
-
-REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const short *Val) {
-  std::string Str;
-  llvm::raw_string_ostream SS(Str);
-  SS << *Val;
-  return Str;
-}
-
-REPL_EXTERNAL_VISIBILITY
-std::string PrintValueRuntime(const unsigned short *Val) {
-  std::string Str;
-  llvm::raw_string_ostream SS(Str);
-  SS << *Val;
-  return Str;
-}
-
-REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const int *Val) {
-  std::string Str;
-  llvm::raw_string_ostream SS(Str);
-  SS << *Val;
-  return Str;
-}
-
-REPL_EXTERNAL_VISIBILITY
-std::string PrintValueRuntime(const unsigned int *Val) {
-  std::string Str;
-  llvm::raw_string_ostream SS(Str);
-  SS << *Val;
-  return Str;
-}
-
-REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const long *Val) {
-  std::string Str;
-  llvm::raw_string_ostream SS(Str);
-  SS << *Val;
-  return Str;
-}
-
-REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const long long *Val) {
-  std::string Str;
-  llvm::raw_string_ostream SS(Str);
-  SS << *Val;
-  return Str;
-}
-
-REPL_EXTERNAL_VISIBILITY
-std::string PrintValueRuntime(const unsigned long *Val) {
-  std::string Str;
-  llvm::raw_string_ostream SS(Str);
-  SS << *Val;
-  return Str;
-}
-
-REPL_EXTERNAL_VISIBILITY
-std::string PrintValueRuntime(const unsigned long long *Val) {
-  std::string Str;
-  llvm::raw_string_ostream SS(Str);
-  SS << *Val;
-  return Str;
-}
-
-REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const float *Val) {
-  std::string Str;
-  llvm::raw_string_ostream SS(Str);
-  SS << llvm::format("%#.6g", *Val) << 'f';
-  return Str;
-}
-
-REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const double *Val) {
-  std::string Str;
-  llvm::raw_string_ostream SS(Str);
-  SS << llvm::format("%#.12g", *Val);
-  return Str;
-}
-
-REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const long double *Val) {
-  std::string Str;
-  llvm::raw_string_ostream SS(Str);
-  SS << llvm::format("%#.8Lg", *Val) << 'L';
-  return Str;
-}
-
-REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const char *const *Val) {
-  return PrintString(Val);
-}
-
-REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const char **Val) {
-  return PrintString(Val);
-}
-
 // Check if the user has implemented a function that avoids fallback to
 // defaults.
 static clang::LookupResult LookupUserDefined(Sema &S, QualType QT) {
-  PrintingPolicy Policy = S.getASTContext().getPrintingPolicy();
-  Policy.SuppressElaboration = 1;
-  Policy.SuppressTagKeyword = 1;
-  Policy.FullyQualifiedName = 1;
-  std::string TypeStr = QT.getAsString(Policy);
-  DeclarationName Name = S.PP.getIdentifierInfo("caas_runtime_to_string_" + TypeStr);
-
-  LLVM_DEBUG(llvm::dbgs() << "Looking for user-defined '" << Name <<"'");
-
-  LookupResult R(S, Name, SourceLocation(),Sema::LookupOrdinaryName);
+  DeclarationName Name;
+  ASTContext &Ctx = S.getASTContext();
+  if (S.getLangOpts().CPlusPlus) {
+    if (auto *NSD = dyn_cast_or_null<NamespaceDecl>(LookupNamed(S, "caas")))
+      if (NamedDecl* RtD = LookupNamed(S, "runtime", NSD)) {
+        Name = S.PP.getIdentifierInfo("to_string");
+        LookupResult R(S, Name, SourceLocation(), Sema::LookupOrdinaryName);
+        S.LookupQualifiedName(R, cast<DeclContext>(RtD)->getPrimaryContext());
+        LookupResult::Filter F = R.makeFilter();
+        while (F.hasNext()) {
+          NamedDecl* D = F.next();
+          FunctionDecl *FD = dyn_cast<FunctionDecl>(D);
+          if (!FD && isa<FunctionTemplateDecl>(D))
+            FD = cast<FunctionTemplateDecl>(D)->getTemplatedDecl();
+          if (FD)
+            if (FD->getNumParams() == 1) {
+              QualType ParamTy = FD->getParamDecl(0)->getType();
+              if (ParamTy->isPointerOrReferenceType()) {
+                // It is a uninstantiated template, we can't really match types.
+                if (isa<FunctionTemplateDecl>(D))
+                  continue;
+                ParamTy = ParamTy->getPointeeType();
+                if (Ctx.hasSameUnqualifiedType(ParamTy, QT))
+                  continue;
+              }
+            }
+
+          F.erase();
+        }
+        F.done();
+        return R;
+      }
+  } else {
+    PrintingPolicy Policy = S.getASTContext().getPrintingPolicy();
+    Policy.SuppressElaboration = 1;
+    Policy.SuppressTagKeyword = 1;
+    Policy.FullyQualifiedName = 1;
+    std::string TypeStr = QT.getAsString(Policy);
+    Name = S.PP.getIdentifierInfo("caas_runtime_to_string_" + TypeStr);
+
+    LLVM_DEBUG(llvm::dbgs() << "Looking for user-defined '" << Name << "'");
+  }
+  LookupResult R(S, Name, SourceLocation(), Sema::LookupOrdinaryName);
   S.LookupName(R, S.getCurScope());
   return R;
 }
 
 namespace clang {
 
-std::string Interpreter::ValueDataToString(const Value &V) {
+std::string Interpreter::CallUserSpecifiedPrinter(LookupResult &R, const Value &V) {
+  assert(!R.empty());
+  // if (!R.isSingleResult())
+  //   return "{error: multiple results for '" + R.getLookupName().getAsString() +
+  //     "'}";
+
   Sema &S = getCompilerInstance()->getSema();
   ASTContext &Ctx = S.getASTContext();
 
-  QualType QT = V.getType();
-  QualType DesugaredTy = QT.getDesugaredType(Ctx);
-  QualType NonRefTy = DesugaredTy.getNonReferenceType();
-
-  if ((NonRefTy->isPointerType() || NonRefTy->isMemberPointerType()) &&
-      NonRefTy->getPointeeType()->isFunctionProtoType())
-    return PrintFunction(V, V.getPtr());
-
-  if (NonRefTy->isFunctionType())
-    return PrintFunction(V, &V);
-
-  if (NonRefTy->isEnumeralType())
-    return PrintEnum(V);
-
-  if (NonRefTy->isNullPtrType())
-    return "nullptr\n";
-
-  // If it is a builtin type dispatch to the builtin overloads.
-  if (auto *BT = DesugaredTy.getCanonicalType()->getAs<BuiltinType>()) {
-    switch (BT->getKind()) {
-    default:
-      return "{ error: unknown builtin type '" + std::to_string(BT->getKind()) +" '}";
-    case clang::BuiltinType::Bool: { bool val = V.getBool(); return PrintValueRuntime(&val); } case clang::BuiltinType::Char_S: { char val = V.getChar_S(); return PrintValueRuntime(&val); } case clang::BuiltinType::SChar: { signed char val = V.getSChar(); return PrintValueRuntime(&val); } case clang::BuiltinType::Char_U: { unsigned char val = V.getChar_U(); return PrintValueRuntime(&val); } case clang::BuiltinType::UChar: { unsigned char val = V.getUChar(); return PrintValueRuntime(&val); } case clang::BuiltinType::Short: { short val = V.getShort(); return PrintValueRuntime(&val); } case clang::BuiltinType::UShort: { unsigned short val = V.getUShort(); return PrintValueRuntime(&val); } case clang::BuiltinType::Int: { int val = V.getInt(); return PrintValueRuntime(&val); } case clang::BuiltinType::UInt: { unsigned int val = V.getUInt(); return PrintValueRuntime(&val); } case clang::BuiltinType::Long: { long val = V.getLong(); return PrintValueRuntime(&val); } case clang::BuiltinType::ULong: { unsigned long val = V.getULong(); return PrintValueRuntime(&val); } case clang::BuiltinType::LongLong: { long long val = V.getLongLong(); return PrintValueRuntime(&val); } case clang::BuiltinType::ULongLong: { unsigned long long val = V.getULongLong(); return PrintValueRuntime(&val); } case clang::BuiltinType::Float: { float val = V.getFloat(); return PrintValueRuntime(&val); } case clang::BuiltinType::Double: { double val = V.getDouble(); return PrintValueRuntime(&val); } case clang::BuiltinType::LongDouble: { long double val = V.getLongDouble(); return PrintValueRuntime(&val); }
-    }
-  }
-  if (auto *CXXRD = NonRefTy->getAsCXXRecordDecl())
-    if (CXXRD->isLambda())
-      return PrintAddress(V.getPtr(), '@');
-
-  // FIXME: Add support for custom printers in C.
-  if (NonRefTy->isPointerType()) {
-    if (NonRefTy->getPointeeType()->isCharType())
-      return PrintValueRuntime((const char**)V.getPtrAddress());
-    return PrintValueRuntime(V.getPtr());
-  }
-
+  // Check the found candidates.
   QualType RetTy = Ctx.getPointerType(Ctx.CharTy.withConst());
-
-  LookupResult R = LookupUserDefined(S, QT);
-  if (!R.isSingleResult())
-    return "{error: multiple results for '" + R.getLookupName().getAsString() + "'}";
-
-  if (R.empty()) {
-
-    if (!Ctx.getLangOpts().CPlusPlus)
-      return PrintValueRuntime(V.getPtr());
-
-    if (S.StdNamespace && !StdString) {
-
-      // Only include the header on demand because it's very heavy.
-      auto TUorE = Parse("#include <__clang_interpreter_runtime_printvalue.h>");
-      if (llvm::Error E = TUorE.takeError() ) {
-        llvm::logAllUnhandledErrors(std::move(E), llvm::errs(), "Parsing failed");
-        return "{error: cannot parse system header}";
-      }
-
-      // Find and cache std::string.
-      Decl *StdStringDecl = LookupNamed(S, "string", S.getStdNamespace());
-      assert(StdStringDecl && "Cannot find std::string");
-      const auto *StdStringTyDecl = llvm::dyn_cast<TypeDecl>(StdStringDecl);
-      assert(StdStringTyDecl && "Cannot find type of std::string");
-      RetTy = QualType(StdStringTyDecl->getTypeForDecl(), /*Quals=*/0);
+  if (Ctx.getLangOpts().CPlusPlus) {
+    if (!S.StdNamespace)
+      return "{error: user-defined pretty printers require std::string; consider #include <string> }";
+    // Find and cache std::string.
+    if (S.StdNamespace && !StdString)
+      StdString = LookupNamed(S, "string", S.getStdNamespace());
+    if (StdString) {
+      const auto *StdStringTD = llvm::dyn_cast<TypeDecl>(StdString);
+      RetTy = QualType(StdStringTD->getTypeForDecl(), /*Quals=*/0);
     }
   }
 
-  // All fails then generate a runtime call, this is slow.
-
   // Create the wrapper function.
   DeclarationName DeclName = &Ctx.Idents.get(CreateUniqName(WrapperName));
   QualType FnTy =
@@ -531,48 +360,161 @@ std::string Interpreter::ValueDataToString(const Value &V) {
 
   // FIXME: We still need to understand why we have to get the pointer to the
   // underlying Value storage for this to work reliabily...
-  if (!V.isManuallyAlloc())
-    ValPtr = V.getPtrAddress();
-
-  // // Check if the user has implemented a function that avoids fallback to
-  // // defaults.
-  // if (Ctx.getLangOpts().CPlusPlus) {
-  //   RuntimeCallName = S.PP.getIdentifierInfo("PrintValueRuntime");
-  // } else {
-  //   PrintingPolicy Policy = Ctx.getPrintingPolicy();
-  //   Policy.SuppressElaboration = 1;
-  //   Policy.SuppressTagKeyword = 1;
-  //   Policy.FullyQualifiedName = 1;
-  //   std::string TypeStr = QT.getAsString(Policy);
-  //   printf("KKK %s\n", TypeStr.c_str());
-  //   //      clang::TypeName::getFullyQualifiedName(QT, Ctx, Policy);
-  //   RuntimeCallName =
-  //     S.PP.getIdentifierInfo("caas__runtime__PrintValueRuntime__" + TypeStr);
-  // }
-
-  // LookupResult R(S, RuntimeCallName, SourceLocation(),Sema::LookupOrdinaryName);
-  // S.LookupName(R, S.getCurScope());
-
-  if (llvm::Error E = BuildWrapperBody(R, S, Ctx, WrapperFD, V.getType(), ValPtr)) {
+  // if (!V.isManuallyAlloc())
+  //   ValPtr = V.getPtrAddress();
+
+  if (llvm::Error E =
+          BuildWrapperBody(R, S, Ctx, WrapperFD, V.getType(), ValPtr)) {
     llvm::logAllUnhandledErrors(std::move(E), llvm::errs(),
                                 "Fail to build a wrapper function");
-    return "{Unable to print the value!}";
+    return "{error: unable to print the value}";
   }
 
   auto AddrOrErr = CompileDecl(*this, WrapperFD);
   if (!AddrOrErr)
     llvm::logAllUnhandledErrors(AddrOrErr.takeError(), llvm::errs(),
                                 "Fail to get symbol address");
-  if (auto *Main = AddrOrErr->toPtr<std::string (*)()>())
-    return (*Main)();
+  if (StdString) {
+    if (auto *Main = AddrOrErr->toPtr<std::string (*)()>())
+      return (*Main)();
+  } else {
+    if (auto *Main = AddrOrErr->toPtr<const char* (*)()>())
+      return (*Main)();
+  }
   return "{Unable to print the value!}";
 }
 
+struct ValueRef: public Value {
+  ValueRef(Interpreter *In, void *Ty) : Value(In, Ty) {
+    // Tell the base class to not try to deallocate if it manages the value.
+    IsManuallyAlloc = false;
+  }
+  void setData(long double D) { Data.m_LongDouble = D; }
+};
+
+std::string Interpreter::ValueDataToString(const Value &V) {
+  Sema &S = getCompilerInstance()->getSema();
+  ASTContext &Ctx = S.getASTContext();
+
+  QualType QT = V.getType();
+
+  while (const ConstantArrayType *CAT = Ctx.getAsConstantArrayType(QT)) {
+    std::string result = "{ ";
+    for (unsigned Idx = 0, N = CAT->getZExtSize(); Idx < N; ++Idx) {
+      QualType ElemTy = CAT->getElementType();
+      ValueRef InnerV = ValueRef(this, ElemTy.getAsOpaquePtr());
+      const Type* BaseTy = CAT->getBaseElementTypeUnsafe();
+      if (ElemTy->isBuiltinType()) {
+        // Arrays model builtin types as a pointer to the builtin. We should
+        // build the dereference to the element type if it is stored as a
+        // builtin.
+        InnerV.setData(V.convertTo<long double>());
+      } else {
+        uintptr_t offset = (uintptr_t)V.getPtr() + Idx * Ctx.getTypeSize(BaseTy) / 8;
+        InnerV.setPtr((void*)offset);
+      }
+      result += ValueDataToString(InnerV);
+      if (Idx < N - 1)
+        result += ", ";
+    }
+    result+=" }";
+    return result;
+  }
+
+  QualType DesugaredTy = QT.getDesugaredType(Ctx);
+  QualType NonRefTy = DesugaredTy.getNonReferenceType();
+
+  LookupResult R = LookupUserDefined(S, QT);
+  if (!R.empty())
+    return CallUserSpecifiedPrinter(R, V);
+
+  // If it is a builtin type dispatch to the builtin overloads.
+  if (auto *BT = DesugaredTy.getCanonicalType()->getAs<BuiltinType>()) {
+    std::string str;
+    llvm::raw_string_ostream ss(str);
+    switch (BT->getKind()) {
+    default:
+      return "{ error: unknown builtin type '" + std::to_string(BT->getKind()) +
+             " '}";
+    case clang::BuiltinType::Bool:
+      ss << ((V.getBool()) ? "true" : "false");
+      return str;
+    case clang::BuiltinType::Char_S:
+      ss << V.getChar_S();
+      return str;
+    case clang::BuiltinType::SChar:
+      ss << V.getSChar();
+      return str;
+    case clang::BuiltinType::Char_U:
+      ss << V.getChar_U();
+      return str;
+    case clang::BuiltinType::UChar:
+      ss << V.getUChar();
+      return str;
+    case clang::BuiltinType::Short:
+      ss << V.getShort();
+      return str;
+    case clang::BuiltinType::UShort:
+      ss << V.getUShort();
+      return str;
+    case clang::BuiltinType::Int:
+      ss << V.getInt();
+      return str;
+    case clang::BuiltinType::UInt:
+      ss << V.getUInt();
+      return str;
+    case clang::BuiltinType::Long:
+      ss << V.getLong();
+      return str;
+    case clang::BuiltinType::ULong:
+      ss << V.getULong();
+      return str;
+    case clang::BuiltinType::LongLong:
+      ss << V.getLongLong();
+      return str;
+    case clang::BuiltinType::ULongLong:
+      ss << V.getULongLong();
+      return str;
+    case clang::BuiltinType::Float:
+      ss << llvm::format("%#.6g", V.getFloat()) << 'f';
+      return str;
+    case clang::BuiltinType::Double:
+      ss << llvm::format("%#.12g", V.getDouble());
+      return str;
+    case clang::BuiltinType::LongDouble:
+      ss << llvm::format("%#.8Lg", V.getLongDouble()) << 'L';
+      return str;
+    }
+  }
+
+  if ((NonRefTy->isPointerType() || NonRefTy->isMemberPointerType()) &&
+      NonRefTy->getPointeeType()->isFunctionProtoType())
+    return FunctionToString(V, V.getPtr());
+
+  if (NonRefTy->isFunctionType())
+    return FunctionToString(V, &V);
+
+  if (NonRefTy->isEnumeralType())
+    return EnumToString(V);
+
+  if (NonRefTy->isNullPtrType())
+    return "nullptr\n";
+
+  // FIXME: Add support for custom printers in C.
+  if (NonRefTy->isPointerType()) {
+    if (NonRefTy->getPointeeType()->isCharType())
+      return AddressToString((const char **)V.getPtrAddress(), '@');
+  }
+
+  // Fall back to printing just the address of the unknown object.
+  return AddressToString(V.getPtr(), '@');
+}
+
 std::string Interpreter::ValueTypeToString(const Value &V) const {
   ASTContext &Ctx = const_cast<ASTContext &>(V.getASTContext());
   QualType QT = V.getType();
 
-  std::string QTStr = PrintQualType(Ctx, QT);
+  std::string QTStr = QualTypeToString(Ctx, QT);
 
   if (QT->isReferenceType())
     QTStr += " &";
@@ -622,7 +564,8 @@ class InterfaceKindVisitor
   InterfaceKind VisitRecordType(const RecordType *Ty) {
     if (S.getLangOpts().CPlusPlus)
       return InterfaceKind::WithAlloc;
-    ExprResult AddrOfE = S.CreateBuiltinUnaryOp(SourceLocation(), UO_AddrOf, E->IgnoreImpCasts());
+    ExprResult AddrOfE = S.CreateBuiltinUnaryOp(SourceLocation(), UO_AddrOf,
+                                                E->IgnoreImpCasts());
     assert(!AddrOfE.isInvalid() && "Can not create unary expression");
     Args.push_back(AddrOfE.get());
     return InterfaceKind::NoAlloc;
@@ -697,9 +640,9 @@ class InterfaceKindVisitor
 };
 
 static constexpr llvm::StringRef VPName[] = {
-  "__clang_Interpreter_SetValueNoAlloc",
-  "__clang_Interpreter_SetValueWithAlloc",
-  "__clang_Interpreter_SetValueCopyArr", "__ci_newtag"};
+    "__clang_Interpreter_SetValueNoAlloc",
+    "__clang_Interpreter_SetValueWithAlloc",
+    "__clang_Interpreter_SetValueCopyArr", "__ci_newtag"};
 
 // This synthesizes a call expression to a speciall
 // function that is responsible for generating the Value.
@@ -798,8 +741,9 @@ llvm::Expected<Expr *> Interpreter::convertExprToValue(Expr *E) {
         S.ActOnCallExpr(Scope, ValuePrintingInfo[InterfaceKind::WithAlloc],
                         E->getBeginLoc(), AdjustedArgs, E->getEndLoc());
     if (AllocCall.isInvalid())
-      return llvm::make_error<llvm::StringError>("Cannot call to " + VPName[WithAlloc],
-                          llvm::inconvertibleErrorCode());
+      return llvm::make_error<llvm::StringError>(
+          "Cannot call to " + VPName[WithAlloc],
+          llvm::inconvertibleErrorCode());
 
     TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ty, SourceLocation());
 
@@ -822,10 +766,11 @@ llvm::Expected<Expr *> Interpreter::convertExprToValue(Expr *E) {
           S.ActOnCallExpr(Scope, ValuePrintingInfo[InterfaceKind::CopyArray],
                           SourceLocation(), Args, SourceLocation());
       if (SetValueE.isInvalid())
-        return llvm::make_error<llvm::StringError>("Cannot call to " + VPName[CopyArray],
-                                                   llvm::inconvertibleErrorCode());
+        return llvm::make_error<llvm::StringError>(
+            "Cannot call to " + VPName[CopyArray],
+            llvm::inconvertibleErrorCode());
       break;
-      //return SetValueE.get();
+      // return SetValueE.get();
     }
     Expr *Args[] = {AllocCall.get(), ValuePrintingInfo[InterfaceKind::NewTag]};
     ExprResult CXXNewCall = S.BuildCXXNew(
@@ -837,8 +782,8 @@ llvm::Expected<Expr *> Interpreter::convertExprToValue(Expr *E) {
 
     if (CXXNewCall.isInvalid())
       return llvm::make_error<llvm::StringError>(
-                          "Cannot build a call to placement new",
-                          llvm::inconvertibleErrorCode());
+          "Cannot build a call to placement new",
+          llvm::inconvertibleErrorCode());
 
     SetValueE = S.ActOnFinishFullExpr(CXXNewCall.get(),
                                       /*DiscardedValue=*/false);
diff --git a/clang/test/Interpreter/pretty-print.cpp b/clang/test/Interpreter/pretty-print.cpp
index 87f595fd03360b..6e15a8855db42e 100644
--- a/clang/test/Interpreter/pretty-print.cpp
+++ b/clang/test/Interpreter/pretty-print.cpp
@@ -11,7 +11,7 @@ struct NonPOD {        \
   NonPOD(): I(sI++) {} \
 };
 namespace caas { namespace runtime {                            \
-  const char* PrintValueRuntime(const NonPOD* type) {           \
+  const char* to_string(const NonPOD* type) {                   \
   switch (type->I) {                                            \
   default: return "out-of-bounds";                              \
   case 0: return "0"; case 1: return "1"; case 2: return "2";   \
@@ -27,6 +27,10 @@ NonPOD non_pod_arr[2][3];
 non_pod_arr
 // CHECK: (NonPOD[2][3]) { { 0, 1, 2 }, { 3, 4, 5 } }
 
+char ch_arr[2][3][1] = {{{'a'}, {'b'}, {'c'}}, {{'d'}, {'e'}, {'f'}}};
+ch_arr
+// CHECK: (char[2][3][1]) {{{'a'}, {'b'}, {'c'}}, {{'d'}, {'e'}, {'f'}}}
+
 struct S3 { int* p; S3() { p = new int(42); } ~S3() { delete p; } };
 S3{}
 // CHECK-NEXT: (S3) @0x{{[0-9a-f]+}}
@@ -148,7 +152,7 @@ struct MyDate {  \
 #include <string>
 
 namespace caas { namespace runtime {                                    \
-    std::string PrintValueRuntime(const MyDate* d) {                    \
+    std::string to_string(const MyDate* d) {                            \
       std::string result;                                               \
       result = std::to_string(d->year) + '-';                           \
       switch(d->month) { default: result += "invalid month"; break;     \
diff --git a/clang/tools/clang-repl/ClangRepl.cpp b/clang/tools/clang-repl/ClangRepl.cpp
index f4efb5e1b49eec..fcdfdd2b475bfa 100644
--- a/clang/tools/clang-repl/ClangRepl.cpp
+++ b/clang/tools/clang-repl/ClangRepl.cpp
@@ -231,8 +231,8 @@ int main(int argc, const char **argv) {
       llvm::StringRef L = *Line;
       L = L.trim();
       if (L.ends_with("\\")) {
-        // FIXME: Support #ifdef X \ ...
         Input += L.drop_back(1);
+        Input += "\n";
         LE.setPrompt("clang-repl...   ");
         continue;
       }



More information about the cfe-commits mailing list