Skip to content

[clang-repl] Lay the basic infrastructure for pretty printing of types #148701

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Jul 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
39 changes: 25 additions & 14 deletions clang/include/clang/Interpreter/Interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,31 +175,42 @@ 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;
mutable llvm::DenseMap<CXXRecordDecl *, llvm::orc::ExecutorAddr> Dtors;

llvm::SmallVector<Expr *, 4> ValuePrintingInfo;
std::array<Expr *, 4> ValuePrintingInfo = {0};

std::unique_ptr<llvm::orc::LLJITBuilder> JITBuilder;

/// @}
/// @name Value and pretty printing support
/// @{

std::string ValueDataToString(const Value &V) const;
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) const;

/// @}
/// @name Code generation
/// @{
CodeGenerator *getCodeGen(IncrementalAction *Action = nullptr) const;
};
} // namespace clang

Expand Down
10 changes: 5 additions & 5 deletions clang/include/clang/Interpreter/Value.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@

#include "llvm/Config/llvm-config.h" // for LLVM_BUILD_LLVM_DYLIB, LLVM_BUILD_SHARED_LIBS
#include "llvm/Support/Compiler.h"
#include <cassert>
#include <cstdint>

// NOTE: Since the REPL itself could also include this runtime, extreme caution
Expand Down Expand Up @@ -97,6 +98,7 @@ class REPL_EXTERNAL_VISIBILITY Value {
REPL_BUILTIN_TYPES
#undef X
void *m_Ptr;
unsigned char m_RawBits[sizeof(long double) * 8]; // widest type
};

public:
Expand All @@ -111,7 +113,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);
Expand All @@ -124,9 +126,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;

Expand All @@ -140,6 +140,7 @@ class REPL_EXTERNAL_VISIBILITY Value {

void *getPtr() const;
void setPtr(void *Ptr) { Data.m_Ptr = Ptr; }
void setRawBits(void *Ptr, unsigned NBits = sizeof(Storage));

#define X(type, name) \
void set##name(type Val) { Data.m_##name = Val; } \
Expand Down Expand Up @@ -193,7 +194,7 @@ class REPL_EXTERNAL_VISIBILITY Value {
}
};

Interpreter *Interp = nullptr;
const Interpreter *Interp = nullptr;
void *OpaqueType = nullptr;
Storage Data;
Kind ValueKind = K_Unspecified;
Expand All @@ -205,6 +206,5 @@ template <> inline void *Value::as() const {
return Data.m_Ptr;
return (void *)as<uintptr_t>();
}

} // namespace clang
#endif
1 change: 1 addition & 0 deletions clang/lib/Interpreter/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ add_clang_library(clangInterpreter
InterpreterUtils.cpp
RemoteJITUtils.cpp
Value.cpp
InterpreterValuePrinter.cpp
${WASM_SRC}
PARTIAL_SOURCES_INTENDED

Expand Down
43 changes: 26 additions & 17 deletions clang/lib/Interpreter/Interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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: ");
Expand Down Expand Up @@ -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]);
}
Expand All @@ -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, ...);
)";

Expand All @@ -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);
}

Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -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>
Expand Down Expand Up @@ -808,10 +817,10 @@ Interpreter::GenModule(IncrementalAction *Action) {
// sure it always stays empty.
assert(((!CachedInCodeGenModule ||
!getCompilerInstance()->getPreprocessorOpts().Includes.empty()) ||
(CachedInCodeGenModule->empty() &&
CachedInCodeGenModule->global_empty() &&
CachedInCodeGenModule->alias_empty() &&
CachedInCodeGenModule->ifunc_empty())) &&
((CachedInCodeGenModule->empty() &&
CachedInCodeGenModule->global_empty() &&
CachedInCodeGenModule->alias_empty() &&
CachedInCodeGenModule->ifunc_empty()))) &&
"CodeGen wrote to a readonly module");
std::unique_ptr<llvm::Module> M(CG->ReleaseModule());
CG->StartModule("incr_module_" + std::to_string(ID++), M->getContext());
Expand All @@ -828,4 +837,4 @@ CodeGenerator *Interpreter::getCodeGen(IncrementalAction *Action) const {
return nullptr;
return static_cast<CodeGenAction *>(WrappedAct)->getCodeGenerator();
}
} // namespace clang
} // end namespace clang
8 changes: 5 additions & 3 deletions clang/lib/Interpreter/InterpreterUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
//===----------------------------------------------------------------------===//

#include "InterpreterUtils.h"
#include "clang/AST/QualTypeNames.h"

namespace clang {

Expand Down Expand Up @@ -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();

Expand All @@ -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
2 changes: 1 addition & 1 deletion clang/lib/Interpreter/InterpreterUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading