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

>From 114e4ec4194afa865bd22f6d76b29200d231bef9 Mon Sep 17 00:00:00 2001
From: Vassil Vassilev <v.g.vassi...@gmail.com>
Date: Sun, 16 Jul 2023 21:18:26 +0000
Subject: [PATCH 1/3] [clang-repl] Lay the basic infrastructure for pretty
 printing of types.

The idea is to store a type-value pair in clang::Value which is updated by the
interpreter runtime. The class copies builtin types and boxes non-builtin types
to provide some lifetime control.

The patch enables default printers for C and C++ using a very minimalistic
approach. We handle enums, arrays and user types. Once we land this we can focus
on enabling user-defined pretty-printers which take control over printing of 
types

The work started as part of https://reviews.llvm.org/D146809, then we created
a giant in https://github.com/llvm/llvm-project/pull/84769
---
 clang/include/clang/AST/ASTContext.h          |   2 +
 clang/include/clang/Interpreter/Interpreter.h |  36 +-
 clang/include/clang/Interpreter/Value.h       |   7 +-
 clang/lib/Interpreter/CMakeLists.txt          |   1 +
 clang/lib/Interpreter/Interpreter.cpp         |  43 +-
 clang/lib/Interpreter/InterpreterUtils.cpp    |   8 +-
 clang/lib/Interpreter/InterpreterUtils.h      |   2 +-
 .../Interpreter/InterpreterValuePrinter.cpp   | 392 ++++++++++++++++--
 clang/lib/Interpreter/Value.cpp               |  36 +-
 clang/lib/Parse/ParseStmt.cpp                 |   3 +-
 clang/test/Interpreter/pretty-print.c         |  75 +++-
 clang/test/Interpreter/pretty-print.cpp       |  59 +++
 .../unittests/Interpreter/InterpreterTest.cpp |  19 +
 13 files changed, 609 insertions(+), 74 deletions(-)
 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 2b9cd035623cc..f058239aabedc 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -1192,6 +1192,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
   bool isInSameModule(const Module *M1, const Module *M2) const;
 
   TranslationUnitDecl *getTranslationUnitDecl() const {
+    assert(TUDecl->getMostRecentDecl() == TUDecl &&
+           "The active TU is not current one!");
     return TUDecl->getMostRecentDecl();
   }
   void addTranslationUnitDecl() {
diff --git a/clang/include/clang/Interpreter/Interpreter.h 
b/clang/include/clang/Interpreter/Interpreter.h
index 78dff1165dcf5..54a2a301c02df 100644
--- a/clang/include/clang/Interpreter/Interpreter.h
+++ b/clang/include/clang/Interpreter/Interpreter.h
@@ -175,31 +175,41 @@ class Interpreter {
   llvm::Expected<llvm::orc::ExecutorAddr>
   getSymbolAddressFromLinkerName(llvm::StringRef LinkerName) const;
 
-  const llvm::SmallVectorImpl<Expr *> &getValuePrintingInfo() const {
-    return ValuePrintingInfo;
-  }
-
-  Expr *SynthesizeExpr(Expr *E);
+  std::unique_ptr<llvm::Module> GenModule(IncrementalAction *Action = nullptr);
+  PartialTranslationUnit &RegisterPTU(TranslationUnitDecl *TU,
+                                      std::unique_ptr<llvm::Module> M = {},
+                                      IncrementalAction *Action = nullptr);
 
 private:
   size_t getEffectivePTUSize() const;
   void markUserCodeStart();
   llvm::Expected<Expr *> ExtractValueFromExpr(Expr *E);
-  llvm::Expected<llvm::orc::ExecutorAddr> CompileDtorCall(CXXRecordDecl 
*CXXRD);
-
-  CodeGenerator *getCodeGen(IncrementalAction *Action = nullptr) const;
-  std::unique_ptr<llvm::Module> GenModule(IncrementalAction *Action = nullptr);
-  PartialTranslationUnit &RegisterPTU(TranslationUnitDecl *TU,
-                                      std::unique_ptr<llvm::Module> M = {},
-                                      IncrementalAction *Action = nullptr);
 
   // A cache for the compiled destructors used to for de-allocation of managed
   // clang::Values.
   llvm::DenseMap<CXXRecordDecl *, llvm::orc::ExecutorAddr> Dtors;
 
-  llvm::SmallVector<Expr *, 4> ValuePrintingInfo;
+  std::array<Expr *, 4> ValuePrintingInfo;
 
   std::unique_ptr<llvm::orc::LLJITBuilder> JITBuilder;
+
+  /// @}
+  /// @name Value and pretty printing support
+  /// @{
+
+  std::string ValueDataToString(const Value &V);
+  std::string ValueTypeToString(const Value &V) const;
+
+  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.
+  llvm::Expected<llvm::orc::ExecutorAddr> CompileDtorCall(CXXRecordDecl 
*CXXRD);
+
+  /// @}
+  /// @name Code generation
+  /// @{
+  CodeGenerator *getCodeGen(IncrementalAction *Action = nullptr) const;
 };
 } // namespace clang
 
diff --git a/clang/include/clang/Interpreter/Value.h 
b/clang/include/clang/Interpreter/Value.h
index a93c0841915fc..e71e4e37e22f6 100644
--- a/clang/include/clang/Interpreter/Value.h
+++ b/clang/include/clang/Interpreter/Value.h
@@ -119,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();
@@ -205,6 +205,5 @@ template <> inline void *Value::as() const {
     return Data.m_Ptr;
   return (void *)as<uintptr_t>();
 }
-
 } // namespace clang
 #endif
diff --git a/clang/lib/Interpreter/CMakeLists.txt 
b/clang/lib/Interpreter/CMakeLists.txt
index 38cf139fa86a6..70de4a2aaa541 100644
--- a/clang/lib/Interpreter/CMakeLists.txt
+++ b/clang/lib/Interpreter/CMakeLists.txt
@@ -29,6 +29,7 @@ add_clang_library(clangInterpreter
   InterpreterUtils.cpp
   RemoteJITUtils.cpp
   Value.cpp
+  InterpreterValuePrinter.cpp
   ${WASM_SRC}
   PARTIAL_SOURCES_INTENDED
 
diff --git a/clang/lib/Interpreter/Interpreter.cpp 
b/clang/lib/Interpreter/Interpreter.cpp
index ed3bae59a144c..e3e8b5f003124 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -264,7 +264,7 @@ class InProcessPrintingASTConsumer final : public 
MultiplexConsumer {
       if (auto *TLSD = llvm::dyn_cast<TopLevelStmtDecl>(D))
         if (TLSD && TLSD->isSemiMissing()) {
           auto ExprOrErr =
-              Interp.ExtractValueFromExpr(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: ");
@@ -440,11 +440,10 @@ const char *const Runtimes = R"(
     #define __CLANG_REPL__ 1
 #ifdef __cplusplus
     #define EXTERN_C extern "C"
-    void *__clang_Interpreter_SetValueWithAlloc(void*, void*, void*);
     struct __clang_Interpreter_NewTag{} __ci_newtag;
     void* operator new(__SIZE_TYPE__, void* __p, __clang_Interpreter_NewTag) 
noexcept;
     template <class T, class = T (*)() /*disable for arrays*/>
-    void __clang_Interpreter_SetValueCopyArr(T* Src, void* Placement, unsigned 
long Size) {
+    void __clang_Interpreter_SetValueCopyArr(const T* Src, void* Placement, 
unsigned long Size) {
       for (auto Idx = 0; Idx < Size; ++Idx)
         new ((void*)(((T*)Placement) + Idx), __ci_newtag) T(Src[Idx]);
     }
@@ -454,8 +453,12 @@ const char *const Runtimes = R"(
     }
 #else
     #define EXTERN_C extern
+    EXTERN_C void *memcpy(void *restrict dst, const void *restrict src, 
__SIZE_TYPE__ n);
+    EXTERN_C inline void __clang_Interpreter_SetValueCopyArr(const void* Src, 
void* Placement, unsigned long Size) {
+      memcpy(Placement, Src, Size);
+    }
 #endif // __cplusplus
-
+  EXTERN_C void *__clang_Interpreter_SetValueWithAlloc(void*, void*, void*);
   EXTERN_C void __clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, 
void *OpaqueType, ...);
 )";
 
@@ -470,12 +473,12 @@ Interpreter::create(std::unique_ptr<CompilerInstance> CI,
 
   // Add runtime code and set a marker to hide it from user code. Undo will not
   // go through that.
-  auto PTU = Interp->Parse(Runtimes);
-  if (!PTU)
-    return PTU.takeError();
+  Err = Interp->ParseAndExecute(Runtimes);
+  if (Err)
+    return std::move(Err);
+
   Interp->markUserCodeStart();
 
-  Interp->ValuePrintingInfo.resize(4);
   return std::move(Interp);
 }
 
@@ -524,12 +527,11 @@ 
Interpreter::createWithCUDA(std::unique_ptr<CompilerInstance> CI,
   return std::move(Interp);
 }
 
+CompilerInstance *Interpreter::getCompilerInstance() { return CI.get(); }
 const CompilerInstance *Interpreter::getCompilerInstance() const {
-  return CI.get();
+  return const_cast<Interpreter *>(this)->getCompilerInstance();
 }
 
-CompilerInstance *Interpreter::getCompilerInstance() { return CI.get(); }
-
 llvm::Expected<llvm::orc::LLJIT &> Interpreter::getExecutionEngine() {
   if (!IncrExecutor) {
     if (auto Err = CreateExecutor())
@@ -610,7 +612,14 @@ Interpreter::Parse(llvm::StringRef Code) {
   if (!TuOrErr)
     return TuOrErr.takeError();
 
-  return RegisterPTU(*TuOrErr);
+  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>
@@ -806,13 +815,13 @@ Interpreter::GenModule(IncrementalAction *Action) {
     // 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 ||
-             !getCompilerInstance()->getPreprocessorOpts().Includes.empty()) ||
-            (CachedInCodeGenModule->empty() &&
+    assert((!CachedInCodeGenModule ||
+            !getCompilerInstance()->getPreprocessorOpts().Includes.empty()) ||
+           ((CachedInCodeGenModule->empty() &&
              CachedInCodeGenModule->global_empty() &&
              CachedInCodeGenModule->alias_empty() &&
              CachedInCodeGenModule->ifunc_empty())) &&
-           "CodeGen wrote to a readonly module");
+               "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;
@@ -828,4 +837,4 @@ CodeGenerator *Interpreter::getCodeGen(IncrementalAction 
*Action) const {
     return nullptr;
   return static_cast<CodeGenAction *>(WrappedAct)->getCodeGenerator();
 }
-} // namespace clang
+} // end namespace clang
diff --git a/clang/lib/Interpreter/InterpreterUtils.cpp 
b/clang/lib/Interpreter/InterpreterUtils.cpp
index 45f6322b8461e..a19f96c80b94f 100644
--- a/clang/lib/Interpreter/InterpreterUtils.cpp
+++ b/clang/lib/Interpreter/InterpreterUtils.cpp
@@ -11,6 +11,7 @@
 
//===----------------------------------------------------------------------===//
 
 #include "InterpreterUtils.h"
+#include "clang/AST/QualTypeNames.h"
 
 namespace clang {
 
@@ -81,7 +82,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 +98,16 @@ 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;
 }
 
 std::string GetFullTypeName(ASTContext &Ctx, QualType QT) {
+  QualType FQT = TypeName::getFullyQualifiedType(QT, Ctx);
   PrintingPolicy Policy(Ctx.getPrintingPolicy());
   Policy.SuppressScope = false;
   Policy.AnonymousTagLocations = false;
-  return QT.getAsString(Policy);
+  return FQT.getAsString(Policy);
 }
 } // namespace clang
diff --git a/clang/lib/Interpreter/InterpreterUtils.h 
b/clang/lib/Interpreter/InterpreterUtils.h
index c7b405b486d93..fbf9814b0d4a7 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);
 
 std::string GetFullTypeName(ASTContext &Ctx, QualType QT);
 } // namespace clang
diff --git a/clang/lib/Interpreter/InterpreterValuePrinter.cpp 
b/clang/lib/Interpreter/InterpreterValuePrinter.cpp
index 3e7e32b2e8557..9edc4b71c6494 100644
--- a/clang/lib/Interpreter/InterpreterValuePrinter.cpp
+++ b/clang/lib/Interpreter/InterpreterValuePrinter.cpp
@@ -18,6 +18,7 @@
 #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"
 
@@ -25,11 +26,334 @@
 #include "llvm/Support/raw_ostream.h"
 
 #include <cassert>
-
+#include <cmath>
 #include <cstdarg>
+#include <sstream>
+#include <string>
+
+#define DEBUG_TYPE "interp-value"
+
+using namespace clang;
+
+static std::string DeclTypeToString(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 QualTypeToString(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 DeclTypeToString(NonRefTy, TTy->getDecl());
+
+  if (const auto *TRy = dyn_cast<RecordType>(NonRefTy))
+    return DeclTypeToString(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 DeclTypeToString(NonRefTy, TDTy->getDecl());
+  }
+  return GetFullTypeName(Ctx, NonRefTy);
+}
+
+static std::string EnumToString(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 << " : " << QualTypeToString(Ctx, ED->getIntegerType()) << " " << APStr;
+  return Str;
+}
+
+static std::string FunctionToString(const Value &V, const void *Ptr) {
+  std::string Str;
+  llvm::raw_string_ostream SS(Str);
+  SS << "Function @" << Ptr;
+
+  const DeclContext *PTU = V.getASTContext().getTranslationUnitDecl();
+  // Find the last top-level-stmt-decl. This is a forward iterator but the
+  // partial translation unit should not be large.
+  const TopLevelStmtDecl *TLSD = nullptr;
+  for (const Decl *D : PTU->noload_decls())
+    if (isa<TopLevelStmtDecl>(D))
+      TLSD = cast<TopLevelStmtDecl>(D);
+
+  // Get __clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void
+  // *OpaqueType, void *Val);
+  const FunctionDecl *FD = nullptr;
+  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 AddressToString(const void *Ptr, char Prefix) {
+  std::string Str;
+  llvm::raw_string_ostream SS(Str);
+  if (!Ptr)
+    return Str;
+  SS << Prefix << Ptr;
+  return Str;
+}
+
+static std::string CharPtrToString(const char *Ptr) {
+  if (!Ptr)
+    return "0";
+
+  std::string Result = "\"";
+  Result += Ptr;
+  Result += '"';
+  return Result;
+}
 
 namespace clang {
 
+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();
+
+  if (const ConstantArrayType *CAT = Ctx.getAsConstantArrayType(QT)) {
+    QualType ElemTy = CAT->getElementType();
+    size_t ElemCount = Ctx.getConstantArrayElementCount(CAT);
+    const Type *BaseTy = CAT->getBaseElementTypeUnsafe();
+    size_t ElemSize = Ctx.getTypeSizeInChars(BaseTy).getQuantity();
+
+    // Treat null terminated char arrays as strings basically.
+    if (ElemTy->isCharType()) {
+      char last = *(char *)(((uintptr_t)V.getPtr()) + ElemCount * ElemSize - 
1);
+      if (last == '\0')
+        return CharPtrToString((char *)V.getPtr());
+    }
+
+    std::string Result = "{ ";
+    for (unsigned Idx = 0, N = CAT->getZExtSize(); Idx < N; ++Idx) {
+      ValueRef InnerV = ValueRef(this, ElemTy.getAsOpaquePtr());
+      if (ElemTy->isBuiltinType()) {
+        // Single dim arrays, advancing.
+        uintptr_t Offset = (uintptr_t)V.getPtr() + Idx * ElemSize;
+        InnerV.setData(*(long double *)Offset);
+      } else {
+        // Multi dim arrays, position to the next dimension.
+        size_t Stride = ElemCount / N;
+        uintptr_t Offset = ((uintptr_t)V.getPtr()) + Idx * Stride * ElemSize;
+        InnerV.setPtr((void *)Offset);
+      }
+
+      Result += ValueDataToString(InnerV);
+
+      // Skip the \0 if the char types
+      if (Idx < N - 1)
+        Result += ", ";
+    }
+    Result += " }";
+    return Result;
+  }
+
+  QualType DesugaredTy = QT.getDesugaredType(Ctx);
+  QualType NonRefTy = DesugaredTy.getNonReferenceType();
+
+  // FIXME: Add support for user defined printers.
+  // 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>()) {
+
+    auto formatFloating = [](auto Val, char Suffix = '\0') -> std::string {
+      std::string Out;
+      llvm::raw_string_ostream SS(Out);
+
+      if (std::isnan(Val) || std::isinf(Val)) {
+        SS << llvm::format("%g", Val);
+        return SS.str();
+      }
+      if (Val == static_cast<decltype(Val)>(static_cast<int64_t>(Val)))
+        SS << llvm::format("%.1f", Val);
+      else if (std::abs(Val) < 1e-4 || std::abs(Val) > 1e6 || Suffix == 'f')
+        SS << llvm::format("%#.6g", Val);
+      else if (Suffix == 'L')
+        SS << llvm::format("%#.12Lg", Val);
+      else
+        SS << llvm::format("%#.8g", Val);
+
+      if (Suffix != '\0')
+        SS << Suffix;
+      return SS.str();
+    };
+
+    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:
+      return formatFloating(V.getFloat(), /*suffix=*/'f');
+
+    case clang::BuiltinType::Double:
+      return formatFloating(V.getDouble());
+
+    case clang::BuiltinType::LongDouble:
+      return formatFloating(V.getLongDouble(), /*suffix=*/'L');
+    }
+  }
+
+  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 CharPtrToString((char *)V.getPtr());
+  }
+
+  // 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 = QualTypeToString(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");
@@ -81,7 +405,7 @@ class InterfaceKindVisitor
     return InterfaceKind::CopyArray;
   }
 
-  InterfaceKind VisitFunctionProtoType(const FunctionProtoType *Ty) {
+  InterfaceKind VisitFunctionType(const FunctionType *Ty) {
     HandlePtrType(Ty);
     return InterfaceKind::NoAlloc;
   }
@@ -141,9 +465,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.
@@ -154,7 +483,7 @@ class InterfaceKindVisitor
 //   // 3. If x is a struct, but a rvalue.
 //   new (__clang_Interpreter_SetValueWithAlloc(ThisInterp, OpaqueValue,
 //   xQualType)) (x);
-llvm::Expected<Expr *> Interpreter::ExtractValueFromExpr(Expr *E) {
+llvm::Expected<Expr *> Interpreter::convertExprToValue(Expr *E) {
   Sema &S = getCompilerInstance()->getSema();
   ASTContext &Ctx = S.getASTContext();
 
@@ -176,23 +505,21 @@ llvm::Expected<Expr *> 
Interpreter::ExtractValueFromExpr(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 (llvm::Error Err =
+            LookupInterface(ValuePrintingInfo[CopyArray], VPName[CopyArray]))
+      return std::move(Err);
+
+    if (llvm::Error Err =
+            LookupInterface(ValuePrintingInfo[WithAlloc], VPName[WithAlloc]))
       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]))
+              LookupInterface(ValuePrintingInfo[NewTag], VPName[NewTag]))
         return std::move(Err);
     }
   }
@@ -211,7 +538,7 @@ llvm::Expected<Expr *> 
Interpreter::ExtractValueFromExpr(Expr *E) {
   if (auto *EWC = llvm::dyn_cast_if_present<ExprWithCleanups>(E))
     E = EWC->getSubExpr();
 
-  QualType Ty = E->getType();
+  QualType Ty = E->IgnoreImpCasts()->getType();
   QualType DesugaredTy = Ty.getDesugaredType(Ctx);
 
   // For lvalue struct, we treat it as a reference.
@@ -239,7 +566,10 @@ llvm::Expected<Expr *> 
Interpreter::ExtractValueFromExpr(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());
 
@@ -253,14 +583,23 @@ llvm::Expected<Expr *> 
Interpreter::ExtractValueFromExpr(Expr *E) {
 
     // __clang_Interpreter_SetValueCopyArr.
     if (Kind == InterfaceKind::CopyArray) {
-      const auto *ConstantArrTy =
-          cast<ConstantArrayType>(DesugaredTy.getTypePtr());
-      size_t ArrSize = Ctx.getConstantArrayElementCount(ConstantArrTy);
+      const auto *CATy = cast<ConstantArrayType>(DesugaredTy.getTypePtr());
+      size_t ArrSize = Ctx.getConstantArrayElementCount(CATy);
+
+      if (!Ctx.getLangOpts().CPlusPlus)
+        ArrSize *= Ctx.getTypeSizeInChars(CATy->getBaseElementTypeUnsafe())
+                       .getQuantity();
+
       Expr *ArrSizeExpr = IntegerLiteralExpr(Ctx, ArrSize);
       Expr *Args[] = {E, AllocCall.get(), ArrSizeExpr};
       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;
     }
     Expr *Args[] = {AllocCall.get(), ValuePrintingInfo[InterfaceKind::NewTag]};
     ExprResult CXXNewCall = S.BuildCXXNew(
@@ -270,8 +609,10 @@ llvm::Expected<Expr *> 
Interpreter::ExtractValueFromExpr(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);
@@ -300,9 +641,8 @@ llvm::Expected<Expr *> 
Interpreter::ExtractValueFromExpr(Expr *E) {
 using namespace clang;
 
 // Temporary rvalue struct that need special care.
-REPL_EXTERNAL_VISIBILITY void *
-__clang_Interpreter_SetValueWithAlloc(void *This, void *OutVal,
-                                      void *OpaqueType) {
+extern "C" void *REPL_EXTERNAL_VISIBILITY 
__clang_Interpreter_SetValueWithAlloc(
+    void *This, void *OutVal, void *OpaqueType) {
   Value &VRef = *(Value *)OutVal;
   VRef = Value(static_cast<Interpreter *>(This), OpaqueType);
   return VRef.getPtr();
diff --git a/clang/lib/Interpreter/Value.cpp b/clang/lib/Interpreter/Value.cpp
index afdf406b37253..dbc0ab3628ae4 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"
@@ -19,6 +20,8 @@
 #include <cassert>
 #include <utility>
 
+using namespace clang;
+
 namespace {
 
 // This is internal buffer maintained by Value, used to hold temporaries.
@@ -118,7 +121,8 @@ static Value::Kind ConvertQualTypeToKind(const ASTContext 
&Ctx, QualType QT) {
 }
 
 Value::Value(Interpreter *In, void *Ty) : Interp(In), OpaqueType(Ty) {
-  setKind(ConvertQualTypeToKind(getASTContext(), getType()));
+  ASTContext &C = getASTContext();
+  setKind(ConvertQualTypeToKind(C, getType()));
   if (ValueKind == K_PtrOrObj) {
     QualType Canon = getType().getCanonicalType();
     if ((Canon->isPointerType() || Canon->isObjectType() ||
@@ -250,17 +254,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 31b84b6f2ede0..bf1978c22ee9f 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -541,7 +541,8 @@ StmtResult Parser::ParseExprStatement(ParsedStmtContext 
StmtCtx) {
   }
 
   Token *CurTok = nullptr;
-  // Note we shouldn't eat the token since the callback needs it.
+  // If the semicolon is missing at the end of REPL input, we want to print
+  // the result. Note we shouldn't eat the token since the callback needs it.
   if (Tok.is(tok::annot_repl_input_end))
     CurTok = &Tok;
   else
diff --git a/clang/test/Interpreter/pretty-print.c 
b/clang/test/Interpreter/pretty-print.c
index d21749a649e1c..47a6746a2c385 100644
--- a/clang/test/Interpreter/pretty-print.c
+++ b/clang/test/Interpreter/pretty-print.c
@@ -3,9 +3,80 @@
 // 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
 
+
+char c = 'a'; c
+// CHECK: (char) 'a'
+
 const char* c_str = "Hello, world!"; c_str
+// CHECK-NEXT: (const char *) "Hello, world!"
+
+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 *) 0
+
+"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.2100000
+
+long double tau = 6.2831853; tau
+// CHECK-NEXT: (long double) 6.28318530000L
+
+int foo() { return 42; } foo()
+// CHECK-NEXT: (int) 42
+
+void bar(int a, float b) {} bar
+// CHECK-NEXT: (void (int, float)) Function @0x{{[0-9a-f]+}}
+// CHECK-NEXT: void bar(int a, float b) {
+
+bar
+// CHECK: (void (int, float)) Function @0x{{[0-9a-f]+}}
+// CHECK-NEXT: void bar(int a, float b) {
+
+// Arrays.
+
+int arr[3] = {1,2,3}; arr
+// CHECK: (int[3]) { 1, 2, 3 }
+
+double darr[3][4] = { {1,2,3,4}, {5,6,7,8}, {9,10,11,12} }; darr
+// CHECK-NEXT: (double[3][4]) { { 1.0, 2.0, 3.0, 4.0 }, { 5.0, 6.0, 7.0, 8.0 
}, { 9.0, 10.0, 11.0, 12.0 } }
+
+float farr[2][1] = { {0}, {3.14}}; farr
+// CHECK-NEXT: (float[2][1]) { { 0.0f }, { 3.14000f } }
+
+0./0.
+// CHECK-NEXT: (double) nan
+
+1.0f / 0.0f
+// CHECK-NEXT: (float) inf
+
+0.00001f
+// CHECK-NEXT: (float) 1.00000e-05f
+
+
+// struct S1{} s1; s1
+// TODO-CHECK-NEXT: (S1 &) @0x{{[0-9a-f]+}}
+
+// struct S2 {int d;} E = {22}; E
+// TODO-CHECK-NEXT: (struct S2 &) @0x{{[0-9a-f]+}}
+// E.d
+// TODO-CHECK-NEXT: (int) 22
 
-// CHECK: Not implement yet.
+%quit
diff --git a/clang/test/Interpreter/pretty-print.cpp 
b/clang/test/Interpreter/pretty-print.cpp
new file mode 100644
index 0000000000000..9dc86c87618c7
--- /dev/null
+++ b/clang/test/Interpreter/pretty-print.cpp
@@ -0,0 +1,59 @@
+// 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*,...);
+
+"ab"
+// CHECK: (const char[3]) "ab"
+
+char ch[2] = {'1','a'}; ch
+// CHECK-NEXT: (char[2]) { '1', 'a' }
+
+char chnull[3] = {'1','a', '\0'}; chnull
+// CHECK-NEXT: (char[3]) "1a"
+
+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]+}}
+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 { R = 0, G, B };
+Color::R
+// CHECK-NEXT: (Color) (Color::R) : 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]+}}
+
+%quit
+
diff --git a/clang/unittests/Interpreter/InterpreterTest.cpp 
b/clang/unittests/Interpreter/InterpreterTest.cpp
index b97f5ae17c9f0..1c6f70c089643 100644
--- a/clang/unittests/Interpreter/InterpreterTest.cpp
+++ b/clang/unittests/Interpreter/InterpreterTest.cpp
@@ -389,6 +389,25 @@ TEST_F(InterpreterTest, Value) {
   EXPECT_TRUE(V9.getType()->isMemberFunctionPointerType());
   EXPECT_EQ(V9.getKind(), Value::K_PtrOrObj);
   EXPECT_TRUE(V9.isManuallyAlloc());
+
+  Value V10;
+  llvm::cantFail(Interp->ParseAndExecute("enum D {Zero = 0, One}; One", &V10));
+
+  std::string prettyType;
+  llvm::raw_string_ostream OSType(prettyType);
+  V10.printType(OSType);
+  EXPECT_STREQ(prettyType.c_str(), "D");
+
+  // FIXME: We should print only the value or the constant not the type.
+  std::string prettyData;
+  llvm::raw_string_ostream OSData(prettyData);
+  V10.printData(OSData);
+  EXPECT_STREQ(prettyData.c_str(), "(One) : unsigned int 1");
+
+  std::string prettyPrint;
+  llvm::raw_string_ostream OSPrint(prettyPrint);
+  V10.print(OSPrint);
+  EXPECT_STREQ(prettyPrint.c_str(), "(D) (One) : unsigned int 1\n");
 }
 
 TEST_F(InterpreterTest, TranslationUnit_CanonicalDecl) {

>From 25e2ff3b98ac56ee562134b5d4db4a3015b02a32 Mon Sep 17 00:00:00 2001
From: Vassil Vassilev <v.g.vassi...@gmail.com>
Date: Tue, 15 Jul 2025 04:54:36 +0000
Subject: [PATCH 2/3] constify

---
 clang/include/clang/Interpreter/Interpreter.h     | 2 +-
 clang/include/clang/Interpreter/Value.h           | 6 +++---
 clang/lib/Interpreter/InterpreterValuePrinter.cpp | 5 +++--
 clang/lib/Interpreter/Value.cpp                   | 6 +++---
 4 files changed, 10 insertions(+), 9 deletions(-)

diff --git a/clang/include/clang/Interpreter/Interpreter.h 
b/clang/include/clang/Interpreter/Interpreter.h
index 54a2a301c02df..b5bc72da6afe0 100644
--- a/clang/include/clang/Interpreter/Interpreter.h
+++ b/clang/include/clang/Interpreter/Interpreter.h
@@ -197,7 +197,7 @@ class Interpreter {
   /// @name Value and pretty printing support
   /// @{
 
-  std::string ValueDataToString(const Value &V);
+  std::string ValueDataToString(const Value &V) const;
   std::string ValueTypeToString(const Value &V) const;
 
   llvm::Expected<Expr *> convertExprToValue(Expr *E);
diff --git a/clang/include/clang/Interpreter/Value.h 
b/clang/include/clang/Interpreter/Value.h
index e71e4e37e22f6..2289eb1bde3ac 100644
--- a/clang/include/clang/Interpreter/Value.h
+++ b/clang/include/clang/Interpreter/Value.h
@@ -119,9 +119,9 @@ class REPL_EXTERNAL_VISIBILITY Value {
   ~Value();
 
   void printType(llvm::raw_ostream &Out) const;
-  void printData(llvm::raw_ostream &Out);
-  void print(llvm::raw_ostream &Out);
-  void dump();
+  void printData(llvm::raw_ostream &Out) const;
+  void print(llvm::raw_ostream &Out) const;
+  void dump() const;
   void clear();
 
   ASTContext &getASTContext();
diff --git a/clang/lib/Interpreter/InterpreterValuePrinter.cpp 
b/clang/lib/Interpreter/InterpreterValuePrinter.cpp
index 9edc4b71c6494..96e575c57317d 100644
--- a/clang/lib/Interpreter/InterpreterValuePrinter.cpp
+++ b/clang/lib/Interpreter/InterpreterValuePrinter.cpp
@@ -180,14 +180,15 @@ static std::string CharPtrToString(const char *Ptr) {
 namespace clang {
 
 struct ValueRef : public Value {
-  ValueRef(Interpreter *In, void *Ty) : Value(In, Ty) {
+  ValueRef(const Interpreter *In, void *Ty)
+      : Value(const_cast<Interpreter *>(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) {
+std::string Interpreter::ValueDataToString(const Value &V) const {
   Sema &S = getCompilerInstance()->getSema();
   ASTContext &Ctx = S.getASTContext();
 
diff --git a/clang/lib/Interpreter/Value.cpp b/clang/lib/Interpreter/Value.cpp
index dbc0ab3628ae4..591eccc415ce4 100644
--- a/clang/lib/Interpreter/Value.cpp
+++ b/clang/lib/Interpreter/Value.cpp
@@ -254,18 +254,18 @@ const ASTContext &Value::getASTContext() const {
   return getInterpreter().getASTContext();
 }
 
-void Value::dump() { print(llvm::outs()); }
+void Value::dump() const { print(llvm::outs()); }
 
 void Value::printType(llvm::raw_ostream &Out) const {
   Out << Interp->ValueTypeToString(*this);
 }
 
-void Value::printData(llvm::raw_ostream &Out) {
+void Value::printData(llvm::raw_ostream &Out) const {
   Out << Interp->ValueDataToString(*this);
 }
 // FIXME: We do not support the multiple inheritance case where one of the base
 // classes has a pretty-printer and the other does not.
-void Value::print(llvm::raw_ostream &Out) {
+void Value::print(llvm::raw_ostream &Out) const {
   assert(OpaqueType != nullptr && "Can't print default Value");
 
   // Don't even try to print a void or an invalid type, it doesn't make sense.

>From 812ddfe4b03a1a838e666190762017b3aa7395f1 Mon Sep 17 00:00:00 2001
From: Vassil Vassilev <v.g.vassi...@gmail.com>
Date: Tue, 15 Jul 2025 05:06:28 +0000
Subject: [PATCH 3/3] Another take on const

---
 clang/include/clang/Interpreter/Interpreter.h     |  5 +++--
 clang/include/clang/Interpreter/Value.h           |  6 ++----
 clang/lib/Interpreter/InterpreterValuePrinter.cpp |  5 ++---
 clang/lib/Interpreter/Value.cpp                   | 14 +++-----------
 4 files changed, 10 insertions(+), 20 deletions(-)

diff --git a/clang/include/clang/Interpreter/Interpreter.h 
b/clang/include/clang/Interpreter/Interpreter.h
index b5bc72da6afe0..8d37bb274b6e3 100644
--- a/clang/include/clang/Interpreter/Interpreter.h
+++ b/clang/include/clang/Interpreter/Interpreter.h
@@ -187,7 +187,7 @@ class Interpreter {
 
   // A cache for the compiled destructors used to for de-allocation of managed
   // clang::Values.
-  llvm::DenseMap<CXXRecordDecl *, llvm::orc::ExecutorAddr> Dtors;
+  mutable llvm::DenseMap<CXXRecordDecl *, llvm::orc::ExecutorAddr> Dtors;
 
   std::array<Expr *, 4> ValuePrintingInfo;
 
@@ -204,7 +204,8 @@ class Interpreter {
 
   // 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);
+  llvm::Expected<llvm::orc::ExecutorAddr>
+  CompileDtorCall(CXXRecordDecl *CXXRD) const;
 
   /// @}
   /// @name Code generation
diff --git a/clang/include/clang/Interpreter/Value.h 
b/clang/include/clang/Interpreter/Value.h
index 2289eb1bde3ac..c969eafff9b31 100644
--- a/clang/include/clang/Interpreter/Value.h
+++ b/clang/include/clang/Interpreter/Value.h
@@ -111,7 +111,7 @@ class REPL_EXTERNAL_VISIBILITY Value {
   };
 
   Value() = default;
-  Value(Interpreter *In, void *Ty);
+  Value(const Interpreter *In, void *Ty);
   Value(const Value &RHS);
   Value(Value &&RHS) noexcept;
   Value &operator=(const Value &RHS);
@@ -124,9 +124,7 @@ class REPL_EXTERNAL_VISIBILITY Value {
   void dump() const;
   void clear();
 
-  ASTContext &getASTContext();
   const ASTContext &getASTContext() const;
-  Interpreter &getInterpreter();
   const Interpreter &getInterpreter() const;
   QualType getType() const;
 
@@ -193,7 +191,7 @@ class REPL_EXTERNAL_VISIBILITY Value {
     }
   };
 
-  Interpreter *Interp = nullptr;
+  const Interpreter *Interp = nullptr;
   void *OpaqueType = nullptr;
   Storage Data;
   Kind ValueKind = K_Unspecified;
diff --git a/clang/lib/Interpreter/InterpreterValuePrinter.cpp 
b/clang/lib/Interpreter/InterpreterValuePrinter.cpp
index 96e575c57317d..32cece177bd6b 100644
--- a/clang/lib/Interpreter/InterpreterValuePrinter.cpp
+++ b/clang/lib/Interpreter/InterpreterValuePrinter.cpp
@@ -180,8 +180,7 @@ static std::string CharPtrToString(const char *Ptr) {
 namespace clang {
 
 struct ValueRef : public Value {
-  ValueRef(const Interpreter *In, void *Ty)
-      : Value(const_cast<Interpreter *>(In), Ty) {
+  ValueRef(const Interpreter *In, void *Ty) : Value(In, Ty) {
     // Tell the base class to not try to deallocate if it manages the value.
     IsManuallyAlloc = false;
   }
@@ -356,7 +355,7 @@ std::string Interpreter::ValueTypeToString(const Value &V) 
const {
 }
 
 llvm::Expected<llvm::orc::ExecutorAddr>
-Interpreter::CompileDtorCall(CXXRecordDecl *CXXRD) {
+Interpreter::CompileDtorCall(CXXRecordDecl *CXXRD) const {
   assert(CXXRD && "Cannot compile a destructor for a nullptr");
   if (auto Dtor = Dtors.find(CXXRD); Dtor != Dtors.end())
     return Dtor->getSecond();
diff --git a/clang/lib/Interpreter/Value.cpp b/clang/lib/Interpreter/Value.cpp
index 591eccc415ce4..c36374fdadca7 100644
--- a/clang/lib/Interpreter/Value.cpp
+++ b/clang/lib/Interpreter/Value.cpp
@@ -120,8 +120,8 @@ static Value::Kind ConvertQualTypeToKind(const ASTContext 
&Ctx, QualType QT) {
   }
 }
 
-Value::Value(Interpreter *In, void *Ty) : Interp(In), OpaqueType(Ty) {
-  ASTContext &C = getASTContext();
+Value::Value(const Interpreter *In, void *Ty) : Interp(In), OpaqueType(Ty) {
+  const ASTContext &C = getASTContext();
   setKind(ConvertQualTypeToKind(C, getType()));
   if (ValueKind == K_PtrOrObj) {
     QualType Canon = getType().getCanonicalType();
@@ -131,7 +131,7 @@ Value::Value(Interpreter *In, void *Ty) : Interp(In), 
OpaqueType(Ty) {
          Canon->isMemberPointerType())) {
       IsManuallyAlloc = true;
       // Compile dtor function.
-      Interpreter &Interp = getInterpreter();
+      const Interpreter &Interp = getInterpreter();
       void *DtorF = nullptr;
       size_t ElementsSize = 1;
       QualType DtorTy = getType();
@@ -236,20 +236,12 @@ QualType Value::getType() const {
   return QualType::getFromOpaquePtr(OpaqueType);
 }
 
-Interpreter &Value::getInterpreter() {
-  assert(Interp != nullptr &&
-         "Can't get interpreter from a default constructed value");
-  return *Interp;
-}
-
 const Interpreter &Value::getInterpreter() const {
   assert(Interp != nullptr &&
          "Can't get interpreter from a default constructed value");
   return *Interp;
 }
 
-ASTContext &Value::getASTContext() { return getInterpreter().getASTContext(); }
-
 const ASTContext &Value::getASTContext() const {
   return getInterpreter().getASTContext();
 }

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to