aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/Interpreter
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/Interpreter')
-rw-r--r--clang/lib/Interpreter/CMakeLists.txt1
-rw-r--r--clang/lib/Interpreter/Interpreter.cpp55
-rw-r--r--clang/lib/Interpreter/InterpreterUtils.cpp8
-rw-r--r--clang/lib/Interpreter/InterpreterUtils.h2
-rw-r--r--clang/lib/Interpreter/InterpreterValuePrinter.cpp395
-rw-r--r--clang/lib/Interpreter/Value.cpp47
6 files changed, 445 insertions, 63 deletions
diff --git a/clang/lib/Interpreter/CMakeLists.txt b/clang/lib/Interpreter/CMakeLists.txt
index 38cf139..70de4a2 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 ed3bae5..9b71486 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -264,7 +264,7 @@ public:
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>
@@ -752,10 +761,18 @@ Interpreter::getSymbolAddressFromLinkerName(llvm::StringRef Name) const {
llvm::Error Interpreter::Undo(unsigned N) {
- if (N > getEffectivePTUSize())
+ if (getEffectivePTUSize() == 0) {
return llvm::make_error<llvm::StringError>("Operation failed. "
- "Too many undos",
+ "No input left to undo",
std::error_code());
+ } else if (N > getEffectivePTUSize()) {
+ return llvm::make_error<llvm::StringError>(
+ llvm::formatv(
+ "Operation failed. Wanted to undo {0} inputs, only have {1}.", N,
+ getEffectivePTUSize()),
+ std::error_code());
+ }
+
for (unsigned I = 0; I < N; I++) {
if (IncrExecutor) {
if (llvm::Error Err = IncrExecutor->removeModule(PTUs.back()))
@@ -808,10 +825,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());
@@ -828,4 +845,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 45f6322..a19f96c 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 c7b405b..fbf9814 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 3e7e32b..0ea6274 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,13 +26,335 @@
#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.convertTo<uint64_t>();
+ 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 VoidPtrToString(const void *Ptr) {
+ std::string Str;
+ llvm::raw_string_ostream SS(Str);
+ SS << 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(const Interpreter *In, void *Ty) : Value(In, Ty) {
+ // Tell the base class to not try to deallocate if it manages the value.
+ IsManuallyAlloc = false;
+ }
+};
+
+std::string Interpreter::ValueDataToString(const Value &V) const {
+ 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.setRawBits((void *)Offset, ElemSize * 8);
+ } 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());
+
+ return VoidPtrToString(V.getPtr());
+ }
+
+ // Fall back to printing just the address of the unknown object.
+ return "@" + VoidPtrToString(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) {
+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();
@@ -81,7 +404,7 @@ public:
return InterfaceKind::CopyArray;
}
- InterfaceKind VisitFunctionProtoType(const FunctionProtoType *Ty) {
+ InterfaceKind VisitFunctionType(const FunctionType *Ty) {
HandlePtrType(Ty);
return InterfaceKind::NoAlloc;
}
@@ -141,9 +464,14 @@ private:
}
};
+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 +482,7 @@ private:
// // 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 +504,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 +537,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 +565,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 +582,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 +608,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,6 +640,7 @@ llvm::Expected<Expr *> Interpreter::ExtractValueFromExpr(Expr *E) {
using namespace clang;
// Temporary rvalue struct that need special care.
+extern "C" {
REPL_EXTERNAL_VISIBILITY void *
__clang_Interpreter_SetValueWithAlloc(void *This, void *OutVal,
void *OpaqueType) {
@@ -308,8 +649,9 @@ __clang_Interpreter_SetValueWithAlloc(void *This, void *OutVal,
return VRef.getPtr();
}
-extern "C" void REPL_EXTERNAL_VISIBILITY __clang_Interpreter_SetValueNoAlloc(
- void *This, void *OutVal, void *OpaqueType, ...) {
+REPL_EXTERNAL_VISIBILITY void
+__clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void *OpaqueType,
+ ...) {
Value &VRef = *(Value *)OutVal;
Interpreter *I = static_cast<Interpreter *>(This);
VRef = Value(I, OpaqueType);
@@ -384,6 +726,7 @@ extern "C" void REPL_EXTERNAL_VISIBILITY __clang_Interpreter_SetValueNoAlloc(
}
va_end(args);
}
+}
// A trampoline to work around the fact that operator placement new cannot
// really be forward declared due to libc++ and libstdc++ declaration mismatch.
diff --git a/clang/lib/Interpreter/Value.cpp b/clang/lib/Interpreter/Value.cpp
index afdf406..be2ab55 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.
@@ -117,8 +120,9 @@ static Value::Kind ConvertQualTypeToKind(const ASTContext &Ctx, QualType QT) {
}
}
-Value::Value(Interpreter *In, void *Ty) : Interp(In), OpaqueType(Ty) {
- setKind(ConvertQualTypeToKind(getASTContext(), getType()));
+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();
if ((Canon->isPointerType() || Canon->isObjectType() ||
@@ -127,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();
@@ -228,14 +232,13 @@ void *Value::getPtr() const {
return Data.m_Ptr;
}
-QualType Value::getType() const {
- return QualType::getFromOpaquePtr(OpaqueType);
+void Value::setRawBits(void *Ptr, unsigned NBits /*= sizeof(Storage)*/) {
+ assert(NBits <= sizeof(Storage) && "Greater than the total size");
+ memcpy(/*dest=*/Data.m_RawBits, /*src=*/Ptr, /*nbytes=*/NBits / 8);
}
-Interpreter &Value::getInterpreter() {
- assert(Interp != nullptr &&
- "Can't get interpreter from a default constructed value");
- return *Interp;
+QualType Value::getType() const {
+ return QualType::getFromOpaquePtr(OpaqueType);
}
const Interpreter &Value::getInterpreter() const {
@@ -244,8 +247,6 @@ const Interpreter &Value::getInterpreter() const {
return *Interp;
}
-ASTContext &Value::getASTContext() { return getInterpreter().getASTContext(); }
-
const ASTContext &Value::getASTContext() const {
return getInterpreter().getASTContext();
}
@@ -253,14 +254,32 @@ const ASTContext &Value::getASTContext() const {
void Value::dump() const { 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";
+ 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) const {
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