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