[clang] [clang-repl] Implement value printing of custom types (PR #84769)
Vassil Vassilev via cfe-commits
cfe-commits at lists.llvm.org
Fri Sep 6 02:00:52 PDT 2024
https://github.com/vgvassilev updated https://github.com/llvm/llvm-project/pull/84769
>From 0f6d68c2c1b2930e74438da67a29307c2e77b383 Mon Sep 17 00:00:00 2001
From: Vassil Vassilev <v.g.vassilev at gmail.com>
Date: Sun, 16 Jul 2023 21:18:26 +0000
Subject: [PATCH 1/3] [clang-repl] Implement value printing of custom types.
The work started as part of https://reviews.llvm.org/D146809
This patch reworks a lot of the infrastructure to enable printing of types for
out-of-process execution on embedded devices.
---
clang/include/clang/AST/ASTContext.h | 2 +
.../clang/Frontend/MultiplexConsumer.h | 3 +-
clang/include/clang/Interpreter/Interpreter.h | 83 +-
clang/include/clang/Interpreter/Value.h | 11 +-
clang/lib/Frontend/MultiplexConsumer.cpp | 7 +
clang/lib/Headers/CMakeLists.txt | 1 +
.../__clang_interpreter_runtime_printvalue.h | 269 ++++++
clang/lib/Interpreter/CMakeLists.txt | 1 +
clang/lib/Interpreter/DeviceOffload.cpp | 10 +-
clang/lib/Interpreter/DeviceOffload.h | 14 +-
clang/lib/Interpreter/IncrementalExecutor.cpp | 2 +-
clang/lib/Interpreter/IncrementalExecutor.h | 4 +-
clang/lib/Interpreter/IncrementalParser.cpp | 253 +----
clang/lib/Interpreter/IncrementalParser.h | 45 +-
clang/lib/Interpreter/Interpreter.cpp | 646 +++++--------
clang/lib/Interpreter/InterpreterUtils.cpp | 395 +++++++-
clang/lib/Interpreter/InterpreterUtils.h | 18 +
.../Interpreter/InterpreterValuePrinter.cpp | 890 ++++++++++++++++++
clang/lib/Interpreter/Value.cpp | 38 +-
clang/lib/Parse/ParseStmt.cpp | 3 +-
clang/test/Interpreter/pretty-print.c | 68 +-
clang/test/Interpreter/pretty-print.cpp | 170 ++++
clang/tools/clang-repl/CMakeLists.txt | 16 +
clang/tools/clang-repl/ClangRepl.cpp | 1 -
.../Interpreter/CodeCompletionTest.cpp | 2 +-
.../Interpreter/InterpreterExtensionsTest.cpp | 68 +-
26 files changed, 2197 insertions(+), 823 deletions(-)
create mode 100644 clang/lib/Headers/__clang_interpreter_runtime_printvalue.h
create mode 100644 clang/lib/Interpreter/InterpreterValuePrinter.cpp
create mode 100644 clang/test/Interpreter/pretty-print.cpp
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 58a820508da42b..c9840820a37ee9 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -1099,6 +1099,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
bool isInSameModule(const Module *M1, const Module *M2);
TranslationUnitDecl *getTranslationUnitDecl() const {
+ assert(TUDecl->getMostRecentDecl() == TUDecl &&
+ "The active TU is not current one!");
return TUDecl->getMostRecentDecl();
}
void addTranslationUnitDecl() {
diff --git a/clang/include/clang/Frontend/MultiplexConsumer.h b/clang/include/clang/Frontend/MultiplexConsumer.h
index 3a7670d7a51aa6..b190750bb29fb8 100644
--- a/clang/include/clang/Frontend/MultiplexConsumer.h
+++ b/clang/include/clang/Frontend/MultiplexConsumer.h
@@ -53,6 +53,7 @@ class MultiplexConsumer : public SemaConsumer {
public:
// Takes ownership of the pointers in C.
MultiplexConsumer(std::vector<std::unique_ptr<ASTConsumer>> C);
+ MultiplexConsumer(std::unique_ptr<ASTConsumer> C);
~MultiplexConsumer() override;
// ASTConsumer
@@ -80,7 +81,7 @@ class MultiplexConsumer : public SemaConsumer {
void InitializeSema(Sema &S) override;
void ForgetSema() override;
-private:
+protected:
std::vector<std::unique_ptr<ASTConsumer>> Consumers; // Owns these.
std::unique_ptr<MultiplexASTMutationListener> MutationListener;
std::unique_ptr<MultiplexASTDeserializationListener> DeserializationListener;
diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h
index 1234608bb58647..195545a5b393b0 100644
--- a/clang/include/clang/Interpreter/Interpreter.h
+++ b/clang/include/clang/Interpreter/Interpreter.h
@@ -14,11 +14,9 @@
#ifndef LLVM_CLANG_INTERPRETER_INTERPRETER_H
#define LLVM_CLANG_INTERPRETER_INTERPRETER_H
-#include "clang/AST/Decl.h"
#include "clang/AST/GlobalDecl.h"
#include "clang/Interpreter/PartialTranslationUnit.h"
#include "clang/Interpreter/Value.h"
-#include "clang/Sema/Ownership.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ExecutionEngine/JITSymbol.h"
@@ -38,6 +36,9 @@ class ThreadSafeContext;
namespace clang {
class CompilerInstance;
+class CodeGenerator;
+class CXXRecordDecl;
+class Decl;
class IncrementalExecutor;
class IncrementalParser;
@@ -77,26 +78,29 @@ class IncrementalCompilerBuilder {
llvm::StringRef CudaSDKPath;
};
-/// Generate glue code between the Interpreter's built-in runtime and user code.
-class RuntimeInterfaceBuilder {
-public:
- virtual ~RuntimeInterfaceBuilder() = default;
-
- using TransformExprFunction = ExprResult(RuntimeInterfaceBuilder *Builder,
- Expr *, ArrayRef<Expr *>);
- virtual TransformExprFunction *getPrintValueTransformer() = 0;
-};
+class IncrementalAction;
+class InProcessPrintingASTConsumer;
/// Provides top-level interfaces for incremental compilation and execution.
class Interpreter {
+ friend class Value;
+ friend InProcessPrintingASTConsumer;
+
std::unique_ptr<llvm::orc::ThreadSafeContext> TSCtx;
+ /// Long-lived, incremental parsing action.
+ std::unique_ptr<IncrementalAction> Act;
std::unique_ptr<IncrementalParser> IncrParser;
std::unique_ptr<IncrementalExecutor> IncrExecutor;
- std::unique_ptr<RuntimeInterfaceBuilder> RuntimeIB;
// An optional parser for CUDA offloading
std::unique_ptr<IncrementalParser> DeviceParser;
+ std::unique_ptr<llvm::orc::LLJITBuilder> JITBuilder;
+
+ /// List containing every information about every incrementally parsed piece
+ /// of code.
+ std::list<PartialTranslationUnit> PTUs;
+
unsigned InitPTUSize = 0;
// This member holds the last result of the value printing. It's a class
@@ -104,15 +108,26 @@ class Interpreter {
// printing happens, it's in an invalid state.
Value LastValue;
- // Add a call to an Expr to report its result. We query the function from
- // RuntimeInterfaceBuilder once and store it as a function pointer to avoid
- // frequent virtual function calls.
- RuntimeInterfaceBuilder::TransformExprFunction *AddPrintValueCall = nullptr;
+ // The cached declaration of std::string used as a return type for the built
+ // trampoline. This is done in C++ to simplify the memory management for
+ // user-defined printing functions.
+ Decl *StdString = nullptr;
+
+ // A cache for the compiled destructors used to for de-allocation of managed
+ // clang::Values.
+ llvm::DenseMap<CXXRecordDecl *, llvm::orc::ExecutorAddr> Dtors;
+
+ std::array<Expr *, 4> ValuePrintingInfo = {0};
+
+ /// When CodeGen is created the first llvm::Module gets cached in many places
+ /// and we must keep it alive.
+ std::unique_ptr<llvm::Module> CachedInCodeGenModule;
protected:
// Derived classes can use an extended interface of the Interpreter.
Interpreter(std::unique_ptr<CompilerInstance> CI, llvm::Error &Err,
- std::unique_ptr<llvm::orc::LLJITBuilder> JITBuilder = nullptr);
+ std::unique_ptr<llvm::orc::LLJITBuilder> JITBuilder = nullptr,
+ std::unique_ptr<clang::ASTConsumer> Consumer = nullptr);
// Create the internal IncrementalExecutor, or re-create it after calling
// ResetExecutor().
@@ -122,15 +137,8 @@ class Interpreter {
// JIT engine. In particular, it doesn't run cleanup or destructors.
void ResetExecutor();
- // Lazily construct the RuntimeInterfaceBuilder. The provided instance will be
- // used for the entire lifetime of the interpreter. The default implementation
- // targets the in-process __clang_Interpreter runtime. Override this to use a
- // custom runtime.
- virtual std::unique_ptr<RuntimeInterfaceBuilder> FindRuntimeInterface();
-
public:
virtual ~Interpreter();
-
static llvm::Expected<std::unique_ptr<Interpreter>>
create(std::unique_ptr<CompilerInstance> CI);
static llvm::Expected<std::unique_ptr<Interpreter>>
@@ -145,7 +153,6 @@ class Interpreter {
llvm::Expected<PartialTranslationUnit &> Parse(llvm::StringRef Code);
llvm::Error Execute(PartialTranslationUnit &T);
llvm::Error ParseAndExecute(llvm::StringRef Code, Value *V = nullptr);
- llvm::Expected<llvm::orc::ExecutorAddr> CompileDtorCall(CXXRecordDecl *CXXRD);
/// Undo N previous incremental inputs.
llvm::Error Undo(unsigned N = 1);
@@ -167,23 +174,29 @@ class Interpreter {
llvm::Expected<llvm::orc::ExecutorAddr>
getSymbolAddressFromLinkerName(llvm::StringRef LinkerName) const;
- enum InterfaceKind { NoAlloc, WithAlloc, CopyArray, NewTag };
-
- const llvm::SmallVectorImpl<Expr *> &getValuePrintingInfo() const {
- return ValuePrintingInfo;
- }
-
- Expr *SynthesizeExpr(Expr *E);
+ std::unique_ptr<llvm::Module> GenModule();
private:
size_t getEffectivePTUSize() const;
void markUserCodeStart();
- llvm::DenseMap<CXXRecordDecl *, llvm::orc::ExecutorAddr> Dtors;
+ /// @}
+ /// @name Value and pretty printing support
+ /// @{
- llvm::SmallVector<Expr *, 4> ValuePrintingInfo;
+ std::string ValueDataToString(const Value &V);
+ std::string ValueTypeToString(const Value &V) const;
- std::unique_ptr<llvm::orc::LLJITBuilder> JITBuilder;
+ llvm::Expected<Expr *> AttachValuePrinting(Expr *E);
+
+ // When we deallocate clang::Value we need to run the destructor of the type.
+ // This function forces emission of the needed dtor.
+ llvm::Expected<llvm::orc::ExecutorAddr> CompileDtorCall(CXXRecordDecl *CXXRD);
+
+ /// @}
+ /// @name Code generation
+ /// @{
+ CodeGenerator *getCodeGen() const;
};
} // namespace clang
diff --git a/clang/include/clang/Interpreter/Value.h b/clang/include/clang/Interpreter/Value.h
index d70e8f8719026b..5417c550e7f985 100644
--- a/clang/include/clang/Interpreter/Value.h
+++ b/clang/include/clang/Interpreter/Value.h
@@ -49,9 +49,10 @@ class raw_ostream;
namespace clang {
class ASTContext;
-class Interpreter;
class QualType;
+class Interpreter;
+
#if defined(_WIN32)
// REPL_EXTERNAL_VISIBILITY are symbols that we need to be able to locate
// at runtime. On Windows, this requires them to be exported from any of the
@@ -118,9 +119,9 @@ class REPL_EXTERNAL_VISIBILITY Value {
~Value();
void printType(llvm::raw_ostream &Out) const;
- void printData(llvm::raw_ostream &Out) const;
- void print(llvm::raw_ostream &Out) const;
- void dump() const;
+ void printData(llvm::raw_ostream &Out);
+ void print(llvm::raw_ostream &Out);
+ void dump();
void clear();
ASTContext &getASTContext();
@@ -138,6 +139,7 @@ class REPL_EXTERNAL_VISIBILITY Value {
void setOpaqueType(void *Ty) { OpaqueType = Ty; }
void *getPtr() const;
+ void **getPtrAddress() const;
void setPtr(void *Ptr) { Data.m_Ptr = Ptr; }
#define X(type, name) \
@@ -204,6 +206,5 @@ template <> inline void *Value::as() const {
return Data.m_Ptr;
return (void *)as<uintptr_t>();
}
-
} // namespace clang
#endif
diff --git a/clang/lib/Frontend/MultiplexConsumer.cpp b/clang/lib/Frontend/MultiplexConsumer.cpp
index 2158d176d18929..3fd3c9bd69037a 100644
--- a/clang/lib/Frontend/MultiplexConsumer.cpp
+++ b/clang/lib/Frontend/MultiplexConsumer.cpp
@@ -298,6 +298,13 @@ MultiplexConsumer::MultiplexConsumer(
}
}
+MultiplexConsumer::MultiplexConsumer(std::unique_ptr<ASTConsumer> C)
+ : MultiplexConsumer([](std::unique_ptr<ASTConsumer> Consumer) {
+ std::vector<std::unique_ptr<ASTConsumer>> Consumers;
+ Consumers.push_back(std::move(Consumer));
+ return Consumers;
+ }(std::move(C))) {}
+
MultiplexConsumer::~MultiplexConsumer() {}
void MultiplexConsumer::Initialize(ASTContext &Context) {
diff --git a/clang/lib/Headers/CMakeLists.txt b/clang/lib/Headers/CMakeLists.txt
index 5a62538792f301..82d07fc77f143b 100644
--- a/clang/lib/Headers/CMakeLists.txt
+++ b/clang/lib/Headers/CMakeLists.txt
@@ -36,6 +36,7 @@ set(core_files
tgmath.h
unwind.h
varargs.h
+ __clang_interpreter_runtime_printvalue.h
)
set(arm_common_files
diff --git a/clang/lib/Headers/__clang_interpreter_runtime_printvalue.h b/clang/lib/Headers/__clang_interpreter_runtime_printvalue.h
new file mode 100644
index 00000000000000..0a1367f970d0fb
--- /dev/null
+++ b/clang/lib/Headers/__clang_interpreter_runtime_printvalue.h
@@ -0,0 +1,269 @@
+//===--- __clang_interpreter_runtime_printvalue.h ---*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines runtime functions used to print STL components in
+// clang-repl. They are very heavy so we should only include it once and on
+// demand.
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_CLANG_INTERPRETER_RUNTIME_PRINT_VALUE_H
+#define LLVM_CLANG_INTERPRETER_RUNTIME_PRINT_VALUE_H
+
+#if !defined(__CLANG_REPL__)
+#error "This file should only be included by clang-repl!"
+#endif
+
+namespace caas {
+namespace runtime {}
+} // namespace caas
+using namespace caas::runtime;
+
+#include <memory>
+#include <string>
+#include <tuple>
+#include <type_traits>
+#include <vector>
+
+// FIXME: We should include it somewhere instead of duplicating it...
+#if __has_attribute(visibility) && \
+ (!(defined(_WIN32) || defined(__CYGWIN__)) || \
+ (defined(__MINGW32__) && defined(__clang__)))
+#if defined(LLVM_BUILD_LLVM_DYLIB) || defined(LLVM_BUILD_SHARED_LIBS)
+#define __REPL_EXTERNAL_VISIBILITY __attribute__((visibility("default")))
+#else
+#define __REPL_EXTERNAL_VISIBILITY
+#endif
+#else
+#if defined(_WIN32)
+#define __REPL_EXTERNAL_VISIBILITY __declspec(dllexport)
+#endif
+#endif
+
+// Fallback.
+template <class T,
+ typename std::enable_if<!std::is_pointer<T>::value>::type * = nullptr>
+inline std::string PrintValueRuntime(const T &) {
+ return "{not representable}";
+}
+
+// Forward declare the pre-compiled printing functions.
+#ifndef __DECL_PRINT_VALUE_RUNTIME
+#define __DECL_PRINT_VALUE_RUNTIME(type) \
+ __REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const type *__Ptr)
+__DECL_PRINT_VALUE_RUNTIME(void); //
+__DECL_PRINT_VALUE_RUNTIME(void *); //
+__DECL_PRINT_VALUE_RUNTIME(char *const); //
+__DECL_PRINT_VALUE_RUNTIME(char *); //
+__DECL_PRINT_VALUE_RUNTIME(bool);
+__DECL_PRINT_VALUE_RUNTIME(char);
+__DECL_PRINT_VALUE_RUNTIME(signed char);
+__DECL_PRINT_VALUE_RUNTIME(short);
+__DECL_PRINT_VALUE_RUNTIME(unsigned short);
+__DECL_PRINT_VALUE_RUNTIME(int);
+__DECL_PRINT_VALUE_RUNTIME(unsigned int);
+__DECL_PRINT_VALUE_RUNTIME(long);
+__DECL_PRINT_VALUE_RUNTIME(unsigned long);
+__DECL_PRINT_VALUE_RUNTIME(long long);
+__DECL_PRINT_VALUE_RUNTIME(unsigned long long);
+__DECL_PRINT_VALUE_RUNTIME(float);
+__DECL_PRINT_VALUE_RUNTIME(double);
+__DECL_PRINT_VALUE_RUNTIME(long double);
+#endif
+
+namespace __repl_runtime_detail {
+
+// Custom void_t implementation for C++11 compatibility
+template <typename... Ts> struct __repl_void_impl {
+ typedef void type;
+};
+
+// Helper to deduce the type of the expression 'std::begin(std::declval<T &>())'
+template <typename T>
+using __repl_begin_result = decltype(std::begin(std::declval<T &>()));
+
+// Helper to deduce the type of the expression 'std::end(std::declval<T &>())'
+template <typename T>
+using __repl_end_result = decltype(std::end(std::declval<T &>()));
+
+// Type trait to check if a type is iterable
+template <typename T, typename = void>
+struct __is_iterable : std::false_type {};
+
+template <typename T>
+struct __is_iterable<T, typename __repl_void_impl<__repl_begin_result<T>,
+ __repl_end_result<T>>::type>
+ : std::true_type {};
+
+// Type trait to check if a type is std::pair
+template <typename T> struct __is_pair : std::false_type {};
+
+template <typename T, typename U>
+struct __is_pair<std::pair<T, U>> : std::true_type {};
+
+// Type trait to check if a type is std::map (or any associative container with
+// mapped_type)
+template <typename T, typename = void> struct __is_map : std::false_type {};
+
+template <typename T>
+struct __is_map<T, typename __repl_void_impl<typename T::mapped_type>::type>
+ : std::true_type {};
+
+// The type of the elements is std::pair, and the container is a map like type.
+template <
+ typename Container, typename Elt,
+ typename std::enable_if<__is_pair<Elt>::value && __is_map<Container>::value,
+ bool>::type = true>
+std::string __PrintCollectionElt(const Elt &Val) {
+ return PrintValueRuntime(&Val.first) + " => " +
+ PrintValueRuntime(&Val.second);
+}
+
+// The type of the elements is std::pair, and the container isn't a map-like
+// type.
+template <typename Container, typename Elt,
+ typename std::enable_if<__is_pair<Elt>::value &&
+ !__is_map<Container>::value,
+ bool>::type = true>
+std::string __PrintCollectionElt(const Elt &Val) {
+ return TuplePairPrintValue(&Val);
+}
+
+template <typename Container, typename Elt,
+ typename std::enable_if<!__is_pair<Elt>::value, bool>::type = true>
+std::string __PrintCollectionElt(const Elt &Val) {
+ return PrintValueRuntime(&Val);
+}
+
+template <class Tuple, std::size_t N = std::tuple_size<Tuple>(),
+ std::size_t TupleSize = std::tuple_size<Tuple>()>
+struct __TupleLikePrinter {
+ static std::string print(const Tuple *T) {
+ constexpr std::size_t EltNum = TupleSize - N;
+ std::string Str;
+ // Not the first element.
+ if (EltNum != 0)
+ Str += ", ";
+ Str += PrintValueRuntime(&std::get<EltNum>(*T));
+ // If N+1 is not smaller than the size of the tuple,
+ // reroute the call to the printing function to the
+ // no-op specialisation to stop recursion.
+ constexpr std::size_t Nm1 = N - 1;
+ Str += __TupleLikePrinter<Tuple, Nm1>::print((const Tuple *)T);
+ return Str;
+ }
+};
+
+template <class Tuple, std::size_t TupleSize>
+struct __TupleLikePrinter<Tuple, 0, TupleSize> {
+ static std::string print(const Tuple *T) { return ""; }
+};
+
+template <class T> inline std::string TuplePairPrintValue(const T *Val) {
+ std::string Str("{ ");
+ Str += __TupleLikePrinter<T>::print(Val);
+ Str += " }";
+ return Str;
+}
+
+struct __StdVectorBool {
+ bool Value;
+ __StdVectorBool(bool V) : Value(V) {}
+};
+template <typename T>
+std::string __PrintCollectionElt(const __StdVectorBool &Val) {
+ return PrintValueRuntime(&Val.Value);
+}
+
+} // namespace __repl_runtime_detail
+
+template <typename Container,
+ typename std::enable_if<
+ __repl_runtime_detail::__is_iterable<Container>::value,
+ bool>::type = true>
+inline std::string PrintValueRuntime(const Container *C) {
+ std::string Str("{ ");
+
+ for (auto Beg = C->begin(), End = C->end(); Beg != End; Beg++) {
+ if (Beg != C->begin())
+ Str += ", ";
+ Str += __repl_runtime_detail::__PrintCollectionElt<Container>(*Beg);
+ }
+ Str += " }";
+ return Str;
+}
+
+template <typename T, size_t N>
+inline std::string PrintValueRuntime(const T (*Obj)[N]) {
+ if (N == 0)
+ return "{}";
+
+ std::string Str = "{ ";
+ for (size_t Idx = 0; Idx < N; ++Idx) {
+ Str += PrintValueRuntime(*Obj + Idx);
+ if (Idx < N - 1)
+ Str += ", ";
+ }
+ return Str + " }";
+}
+
+template <size_t N> inline std::string PrintValueRuntime(const char (*Obj)[N]) {
+ const auto *Str = reinterpret_cast<const char *const>(Obj);
+ return PrintValueRuntime(&Str);
+}
+
+// std::vector<bool>
+inline std::string PrintValueRuntime(const std::vector<bool> *Val) {
+ // Try our best to fix std::vector<bool> without too much of templated code.
+ std::vector<__repl_runtime_detail::__StdVectorBool> ValFixed(Val->begin(),
+ Val->end());
+ return PrintValueRuntime<decltype(ValFixed)>(&ValFixed);
+}
+
+// tuple
+template <typename... Ts>
+inline std::string PrintValueRuntime(const std::tuple<Ts...> *Val) {
+ using T = std::tuple<Ts...>;
+ return __repl_runtime_detail::TuplePairPrintValue<T>(Val);
+}
+
+// pair
+template <typename... Ts>
+inline std::string PrintValueRuntime(const std::pair<Ts...> *Val) {
+ using T = std::pair<Ts...>;
+ return __repl_runtime_detail::TuplePairPrintValue<T>(Val);
+}
+
+// unique_ptr
+template <class T>
+inline std::string PrintValueRuntime(const std::unique_ptr<T> *Val) {
+ auto Ptr = Val->get();
+ return "std::unique_ptr -> " + PrintValueRuntime((const void **)&Ptr);
+}
+
+// shared_ptr
+template <class T>
+inline std::string PrintValueRuntime(const std::shared_ptr<T> *Val) {
+ auto Ptr = Val->get();
+ return "std::shared_ptr -> " + PrintValueRuntime((const void **)&Ptr);
+}
+
+// weak_ptr
+template <class T>
+inline std::string PrintValueRuntime(const std::weak_ptr<T> *Val) {
+ auto Ptr = Val->lock().get();
+ return "std::weak_ptr -> " + PrintValueRuntime((const void **)&Ptr);
+}
+
+// string
+template <class T>
+inline std::string PrintValueRuntime(const std::basic_string<T> *Val) {
+ const char *Chars = Val->c_str();
+ return PrintValueRuntime((const char **)&Chars);
+}
+#endif
diff --git a/clang/lib/Interpreter/CMakeLists.txt b/clang/lib/Interpreter/CMakeLists.txt
index 6a069659ebb8db..8d95b78bd10bee 100644
--- a/clang/lib/Interpreter/CMakeLists.txt
+++ b/clang/lib/Interpreter/CMakeLists.txt
@@ -24,6 +24,7 @@ add_clang_library(clangInterpreter
Interpreter.cpp
InterpreterUtils.cpp
Value.cpp
+ InterpreterValuePrinter.cpp
${WASM_SRC}
PARTIAL_SOURCES_INTENDED
diff --git a/clang/lib/Interpreter/DeviceOffload.cpp b/clang/lib/Interpreter/DeviceOffload.cpp
index 07c9e3005e5fd3..4bf9ed28096893 100644
--- a/clang/lib/Interpreter/DeviceOffload.cpp
+++ b/clang/lib/Interpreter/DeviceOffload.cpp
@@ -15,6 +15,7 @@
#include "clang/Basic/TargetOptions.h"
#include "clang/CodeGen/ModuleBuilder.h"
#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Interpreter/PartialTranslationUnit.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Module.h"
@@ -24,11 +25,10 @@
namespace clang {
IncrementalCUDADeviceParser::IncrementalCUDADeviceParser(
- Interpreter &Interp, std::unique_ptr<CompilerInstance> Instance,
- IncrementalParser &HostParser, llvm::LLVMContext &LLVMCtx,
+ std::unique_ptr<CompilerInstance> Instance, IncrementalParser &HostParser,
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS,
- llvm::Error &Err)
- : IncrementalParser(Interp, std::move(Instance), LLVMCtx, Err),
+ llvm::Error &Err, const std::list<PartialTranslationUnit> &PTUs)
+ : IncrementalParser(std::move(Instance), Err), PTUs(PTUs),
HostParser(HostParser), VFS(FS) {
if (Err)
return;
@@ -41,7 +41,7 @@ IncrementalCUDADeviceParser::IncrementalCUDADeviceParser(
}
}
-llvm::Expected<PartialTranslationUnit &>
+llvm::Expected<TranslationUnitDecl *>
IncrementalCUDADeviceParser::Parse(llvm::StringRef Input) {
auto PTU = IncrementalParser::Parse(Input);
if (!PTU)
diff --git a/clang/lib/Interpreter/DeviceOffload.h b/clang/lib/Interpreter/DeviceOffload.h
index ce4f218c94c79d..b84870474841a5 100644
--- a/clang/lib/Interpreter/DeviceOffload.h
+++ b/clang/lib/Interpreter/DeviceOffload.h
@@ -18,19 +18,19 @@
#include "llvm/Support/VirtualFileSystem.h"
namespace clang {
-
+struct PartialTranslationUnit;
class IncrementalCUDADeviceParser : public IncrementalParser {
+ const std::list<PartialTranslationUnit> &PTUs;
+
public:
IncrementalCUDADeviceParser(
- Interpreter &Interp, std::unique_ptr<CompilerInstance> Instance,
- IncrementalParser &HostParser, llvm::LLVMContext &LLVMCtx,
+ std::unique_ptr<CompilerInstance> Instance, IncrementalParser &HostParser,
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> VFS,
- llvm::Error &Err);
+ llvm::Error &Err, const std::list<PartialTranslationUnit> &PTUs);
- llvm::Expected<PartialTranslationUnit &>
- Parse(llvm::StringRef Input) override;
+ llvm::Expected<TranslationUnitDecl *> Parse(llvm::StringRef Input) override;
- // Generate PTX for the last PTU
+ // Generate PTX for the last PTU.
llvm::Expected<llvm::StringRef> GeneratePTX();
// Generate fatbinary contents in memory
diff --git a/clang/lib/Interpreter/IncrementalExecutor.cpp b/clang/lib/Interpreter/IncrementalExecutor.cpp
index 1824a5b4570a93..4d2adecaafce74 100644
--- a/clang/lib/Interpreter/IncrementalExecutor.cpp
+++ b/clang/lib/Interpreter/IncrementalExecutor.cpp
@@ -118,4 +118,4 @@ IncrementalExecutor::getSymbolAddress(llvm::StringRef Name,
return SymOrErr->getAddress();
}
-} // end namespace clang
+} // namespace clang
diff --git a/clang/lib/Interpreter/IncrementalExecutor.h b/clang/lib/Interpreter/IncrementalExecutor.h
index 7954cde36588bd..4e3afb7de26e00 100644
--- a/clang/lib/Interpreter/IncrementalExecutor.h
+++ b/clang/lib/Interpreter/IncrementalExecutor.h
@@ -32,9 +32,10 @@ class ThreadSafeContext;
namespace clang {
-struct PartialTranslationUnit;
class TargetInfo;
+struct PartialTranslationUnit;
+
class IncrementalExecutor {
using CtorDtorIterator = llvm::orc::CtorDtorIterator;
std::unique_ptr<llvm::orc::LLJIT> Jit;
@@ -65,7 +66,6 @@ class IncrementalExecutor {
static llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>>
createDefaultJITBuilder(llvm::orc::JITTargetMachineBuilder JTMB);
};
-
} // end namespace clang
#endif // LLVM_CLANG_LIB_INTERPRETER_INCREMENTALEXECUTOR_H
diff --git a/clang/lib/Interpreter/IncrementalParser.cpp b/clang/lib/Interpreter/IncrementalParser.cpp
index b7c809c45098ca..615f61e9aec1bc 100644
--- a/clang/lib/Interpreter/IncrementalParser.cpp
+++ b/clang/lib/Interpreter/IncrementalParser.cpp
@@ -13,233 +13,33 @@
#include "IncrementalParser.h"
#include "clang/AST/DeclContextInternals.h"
-#include "clang/CodeGen/BackendUtil.h"
-#include "clang/CodeGen/CodeGenAction.h"
-#include "clang/CodeGen/ModuleBuilder.h"
#include "clang/Frontend/CompilerInstance.h"
-#include "clang/Frontend/FrontendAction.h"
-#include "clang/FrontendTool/Utils.h"
-#include "clang/Interpreter/Interpreter.h"
+#include "clang/Interpreter/PartialTranslationUnit.h"
#include "clang/Parse/Parser.h"
#include "clang/Sema/Sema.h"
-#include "llvm/Option/ArgList.h"
#include "llvm/Support/CrashRecoveryContext.h"
#include "llvm/Support/Error.h"
-#include "llvm/Support/Timer.h"
#include <sstream>
namespace clang {
-class IncrementalASTConsumer final : public ASTConsumer {
- Interpreter &Interp;
- std::unique_ptr<ASTConsumer> Consumer;
-
-public:
- IncrementalASTConsumer(Interpreter &InterpRef, std::unique_ptr<ASTConsumer> C)
- : Interp(InterpRef), Consumer(std::move(C)) {}
-
- bool HandleTopLevelDecl(DeclGroupRef DGR) override final {
- if (DGR.isNull())
- return true;
- if (!Consumer)
- return true;
-
- for (Decl *D : DGR)
- if (auto *TSD = llvm::dyn_cast<TopLevelStmtDecl>(D);
- TSD && TSD->isSemiMissing())
- TSD->setStmt(Interp.SynthesizeExpr(cast<Expr>(TSD->getStmt())));
-
- return Consumer->HandleTopLevelDecl(DGR);
- }
- void HandleTranslationUnit(ASTContext &Ctx) override final {
- Consumer->HandleTranslationUnit(Ctx);
- }
- void HandleInlineFunctionDefinition(FunctionDecl *D) override final {
- Consumer->HandleInlineFunctionDefinition(D);
- }
- void HandleInterestingDecl(DeclGroupRef D) override final {
- Consumer->HandleInterestingDecl(D);
- }
- void HandleTagDeclDefinition(TagDecl *D) override final {
- Consumer->HandleTagDeclDefinition(D);
- }
- void HandleTagDeclRequiredDefinition(const TagDecl *D) override final {
- Consumer->HandleTagDeclRequiredDefinition(D);
- }
- void HandleCXXImplicitFunctionInstantiation(FunctionDecl *D) override final {
- Consumer->HandleCXXImplicitFunctionInstantiation(D);
- }
- void HandleTopLevelDeclInObjCContainer(DeclGroupRef D) override final {
- Consumer->HandleTopLevelDeclInObjCContainer(D);
- }
- void HandleImplicitImportDecl(ImportDecl *D) override final {
- Consumer->HandleImplicitImportDecl(D);
- }
- void CompleteTentativeDefinition(VarDecl *D) override final {
- Consumer->CompleteTentativeDefinition(D);
- }
- void CompleteExternalDeclaration(DeclaratorDecl *D) override final {
- Consumer->CompleteExternalDeclaration(D);
- }
- void AssignInheritanceModel(CXXRecordDecl *RD) override final {
- Consumer->AssignInheritanceModel(RD);
- }
- void HandleCXXStaticMemberVarInstantiation(VarDecl *D) override final {
- Consumer->HandleCXXStaticMemberVarInstantiation(D);
- }
- void HandleVTable(CXXRecordDecl *RD) override final {
- Consumer->HandleVTable(RD);
- }
- ASTMutationListener *GetASTMutationListener() override final {
- return Consumer->GetASTMutationListener();
- }
- ASTDeserializationListener *GetASTDeserializationListener() override final {
- return Consumer->GetASTDeserializationListener();
- }
- void PrintStats() override final { Consumer->PrintStats(); }
- bool shouldSkipFunctionBody(Decl *D) override final {
- return Consumer->shouldSkipFunctionBody(D);
- }
- static bool classof(const clang::ASTConsumer *) { return true; }
-};
-
-/// A custom action enabling the incremental processing functionality.
-///
-/// The usual \p FrontendAction expects one call to ExecuteAction and once it
-/// sees a call to \p EndSourceFile it deletes some of the important objects
-/// such as \p Preprocessor and \p Sema assuming no further input will come.
-///
-/// \p IncrementalAction ensures it keep its underlying action's objects alive
-/// as long as the \p IncrementalParser needs them.
-///
-class IncrementalAction : public WrapperFrontendAction {
-private:
- bool IsTerminating = false;
-
-public:
- IncrementalAction(CompilerInstance &CI, llvm::LLVMContext &LLVMCtx,
- llvm::Error &Err)
- : WrapperFrontendAction([&]() {
- llvm::ErrorAsOutParameter EAO(&Err);
- std::unique_ptr<FrontendAction> Act;
- switch (CI.getFrontendOpts().ProgramAction) {
- default:
- Err = llvm::createStringError(
- std::errc::state_not_recoverable,
- "Driver initialization failed. "
- "Incremental mode for action %d is not supported",
- CI.getFrontendOpts().ProgramAction);
- return Act;
- case frontend::ASTDump:
- [[fallthrough]];
- case frontend::ASTPrint:
- [[fallthrough]];
- case frontend::ParseSyntaxOnly:
- Act = CreateFrontendAction(CI);
- break;
- case frontend::PluginAction:
- [[fallthrough]];
- case frontend::EmitAssembly:
- [[fallthrough]];
- case frontend::EmitBC:
- [[fallthrough]];
- case frontend::EmitObj:
- [[fallthrough]];
- case frontend::PrintPreprocessedInput:
- [[fallthrough]];
- case frontend::EmitLLVMOnly:
- Act.reset(new EmitLLVMOnlyAction(&LLVMCtx));
- break;
- }
- return Act;
- }()) {}
- FrontendAction *getWrapped() const { return WrappedAction.get(); }
- TranslationUnitKind getTranslationUnitKind() override {
- return TU_Incremental;
- }
-
- void ExecuteAction() override {
- CompilerInstance &CI = getCompilerInstance();
- assert(CI.hasPreprocessor() && "No PP!");
-
- // Use a code completion consumer?
- CodeCompleteConsumer *CompletionConsumer = nullptr;
- if (CI.hasCodeCompletionConsumer())
- CompletionConsumer = &CI.getCodeCompletionConsumer();
-
- Preprocessor &PP = CI.getPreprocessor();
- PP.EnterMainSourceFile();
-
- if (!CI.hasSema())
- CI.createSema(getTranslationUnitKind(), CompletionConsumer);
- }
-
- // Do not terminate after processing the input. This allows us to keep various
- // clang objects alive and to incrementally grow the current TU.
- void EndSourceFile() override {
- // The WrappedAction can be nullptr if we issued an error in the ctor.
- if (IsTerminating && getWrapped())
- WrapperFrontendAction::EndSourceFile();
- }
-
- void FinalizeAction() {
- assert(!IsTerminating && "Already finalized!");
- IsTerminating = true;
- EndSourceFile();
- }
-};
-
-CodeGenerator *IncrementalParser::getCodeGen() const {
- FrontendAction *WrappedAct = Act->getWrapped();
- if (!WrappedAct->hasIRSupport())
- return nullptr;
- return static_cast<CodeGenAction *>(WrappedAct)->getCodeGenerator();
-}
-
IncrementalParser::IncrementalParser() {}
-IncrementalParser::IncrementalParser(Interpreter &Interp,
- std::unique_ptr<CompilerInstance> Instance,
- llvm::LLVMContext &LLVMCtx,
+IncrementalParser::IncrementalParser(std::unique_ptr<CompilerInstance> Instance,
llvm::Error &Err)
: CI(std::move(Instance)) {
llvm::ErrorAsOutParameter EAO(&Err);
- Act = std::make_unique<IncrementalAction>(*CI, LLVMCtx, Err);
- if (Err)
- return;
- CI->ExecuteAction(*Act);
- if (getCodeGen())
- CachedInCodeGenModule = GenModule();
-
- std::unique_ptr<ASTConsumer> IncrConsumer =
- std::make_unique<IncrementalASTConsumer>(Interp, CI->takeASTConsumer());
- CI->setASTConsumer(std::move(IncrConsumer));
Consumer = &CI->getASTConsumer();
P.reset(
new Parser(CI->getPreprocessor(), CI->getSema(), /*SkipBodies=*/false));
P->Initialize();
-
- // An initial PTU is needed as CUDA includes some headers automatically
- auto PTU = ParseOrWrapTopLevelDecl();
- if (auto E = PTU.takeError()) {
- consumeError(std::move(E)); // FIXME
- return; // PTU.takeError();
- }
-
- if (getCodeGen()) {
- PTU->TheModule = GenModule();
- assert(PTU->TheModule && "Failed to create initial PTU");
- }
}
-IncrementalParser::~IncrementalParser() {
- P.reset();
- Act->FinalizeAction();
-}
+IncrementalParser::~IncrementalParser() { P.reset(); }
-llvm::Expected<PartialTranslationUnit &>
+llvm::Expected<TranslationUnitDecl *>
IncrementalParser::ParseOrWrapTopLevelDecl() {
// Recover resources if we crash before exiting this method.
Sema &S = CI->getSema();
@@ -247,12 +47,9 @@ IncrementalParser::ParseOrWrapTopLevelDecl() {
Sema::GlobalEagerInstantiationScope GlobalInstantiations(S, /*Enabled=*/true);
Sema::LocalEagerInstantiationScope LocalInstantiations(S);
- PTUs.emplace_back(PartialTranslationUnit());
- PartialTranslationUnit &LastPTU = PTUs.back();
// Add a new PTU.
ASTContext &C = S.getASTContext();
C.addTranslationUnitDecl();
- LastPTU.TUPart = C.getTranslationUnitDecl();
// Skip previous eof due to last incremental input.
if (P->getCurToken().is(tok::annot_repl_input_end)) {
@@ -278,9 +75,7 @@ IncrementalParser::ParseOrWrapTopLevelDecl() {
DiagnosticsEngine &Diags = getCI()->getDiagnostics();
if (Diags.hasErrorOccurred()) {
- PartialTranslationUnit MostRecentPTU = {C.getTranslationUnitDecl(),
- nullptr};
- CleanUpPTU(MostRecentPTU);
+ CleanUpPTU(C.getTranslationUnitDecl());
Diags.Reset(/*soft=*/true);
Diags.getClient()->clear();
@@ -299,10 +94,10 @@ IncrementalParser::ParseOrWrapTopLevelDecl() {
Consumer->HandleTranslationUnit(C);
- return LastPTU;
+ return C.getTranslationUnitDecl();
}
-llvm::Expected<PartialTranslationUnit &>
+llvm::Expected<TranslationUnitDecl *>
IncrementalParser::Parse(llvm::StringRef input) {
Preprocessor &PP = CI->getPreprocessor();
assert(PP.isIncrementalProcessingEnabled() && "Not in incremental mode!?");
@@ -356,37 +151,10 @@ IncrementalParser::Parse(llvm::StringRef input) {
"Lexer must be EOF when starting incremental parse!");
}
- if (std::unique_ptr<llvm::Module> M = GenModule())
- PTU->TheModule = std::move(M);
-
return PTU;
}
-std::unique_ptr<llvm::Module> IncrementalParser::GenModule() {
- static unsigned ID = 0;
- if (CodeGenerator *CG = getCodeGen()) {
- // Clang's CodeGen is designed to work with a single llvm::Module. In many
- // cases for convenience various CodeGen parts have a reference to the
- // llvm::Module (TheModule or Module) which does not change when a new
- // module is pushed. However, the execution engine wants to take ownership
- // of the module which does not map well to CodeGen's design. To work this
- // around we created an empty module to make CodeGen happy. We should make
- // sure it always stays empty.
- assert((!CachedInCodeGenModule ||
- (CachedInCodeGenModule->empty() &&
- CachedInCodeGenModule->global_empty() &&
- CachedInCodeGenModule->alias_empty() &&
- CachedInCodeGenModule->ifunc_empty())) &&
- "CodeGen wrote to a readonly module");
- std::unique_ptr<llvm::Module> M(CG->ReleaseModule());
- CG->StartModule("incr_module_" + std::to_string(ID++), M->getContext());
- return M;
- }
- return nullptr;
-}
-
-void IncrementalParser::CleanUpPTU(PartialTranslationUnit &PTU) {
- TranslationUnitDecl *MostRecentTU = PTU.TUPart;
+void IncrementalParser::CleanUpPTU(TranslationUnitDecl *MostRecentTU) {
if (StoredDeclsMap *Map = MostRecentTU->getPrimaryContext()->getLookupPtr()) {
for (auto &&[Key, List] : *Map) {
DeclContextLookupResult R = List.getLookupResult();
@@ -419,9 +187,4 @@ void IncrementalParser::CleanUpPTU(PartialTranslationUnit &PTU) {
}
}
-llvm::StringRef IncrementalParser::GetMangledName(GlobalDecl GD) const {
- CodeGenerator *CG = getCodeGen();
- assert(CG);
- return CG->GetMangledName(GD);
-}
} // end namespace clang
diff --git a/clang/lib/Interpreter/IncrementalParser.h b/clang/lib/Interpreter/IncrementalParser.h
index f63bce50acd3b9..4ce361143b581b 100644
--- a/clang/lib/Interpreter/IncrementalParser.h
+++ b/clang/lib/Interpreter/IncrementalParser.h
@@ -13,35 +13,24 @@
#ifndef LLVM_CLANG_LIB_INTERPRETER_INCREMENTALPARSER_H
#define LLVM_CLANG_LIB_INTERPRETER_INCREMENTALPARSER_H
-#include "clang/AST/GlobalDecl.h"
-#include "clang/Interpreter/PartialTranslationUnit.h"
-
-#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
#include <list>
#include <memory>
-namespace llvm {
-class LLVMContext;
-class Module;
-} // namespace llvm
namespace clang {
class ASTConsumer;
class CodeGenerator;
class CompilerInstance;
-class IncrementalAction;
-class Interpreter;
class Parser;
+class TranslationUnitDecl;
+
/// Provides support for incremental compilation. Keeps track of the state
/// changes between the subsequent incremental input.
///
class IncrementalParser {
protected:
- /// Long-lived, incremental parsing action.
- std::unique_ptr<IncrementalAction> Act;
-
/// Compiler instance performing the incremental compilation.
std::unique_ptr<CompilerInstance> CI;
@@ -54,42 +43,24 @@ class IncrementalParser {
/// Counts the number of direct user input lines that have been parsed.
unsigned InputCount = 0;
- /// List containing every information about every incrementally parsed piece
- /// of code.
- std::list<PartialTranslationUnit> PTUs;
-
- /// When CodeGen is created the first llvm::Module gets cached in many places
- /// and we must keep it alive.
- std::unique_ptr<llvm::Module> CachedInCodeGenModule;
-
IncrementalParser();
public:
- IncrementalParser(Interpreter &Interp,
- std::unique_ptr<CompilerInstance> Instance,
- llvm::LLVMContext &LLVMCtx, llvm::Error &Err);
+ IncrementalParser(std::unique_ptr<CompilerInstance> Instance,
+ llvm::Error &Err);
virtual ~IncrementalParser();
CompilerInstance *getCI() { return CI.get(); }
- CodeGenerator *getCodeGen() const;
/// Parses incremental input by creating an in-memory file.
///\returns a \c PartialTranslationUnit which holds information about the
- /// \c TranslationUnitDecl and \c llvm::Module corresponding to the input.
- virtual llvm::Expected<PartialTranslationUnit &> Parse(llvm::StringRef Input);
-
- /// Uses the CodeGenModule mangled name cache and avoids recomputing.
- ///\returns the mangled name of a \c GD.
- llvm::StringRef GetMangledName(GlobalDecl GD) const;
-
- void CleanUpPTU(PartialTranslationUnit &PTU);
-
- std::list<PartialTranslationUnit> &getPTUs() { return PTUs; }
+ /// \c TranslationUnitDecl.
+ virtual llvm::Expected<TranslationUnitDecl *> Parse(llvm::StringRef Input);
- std::unique_ptr<llvm::Module> GenModule();
+ void CleanUpPTU(TranslationUnitDecl *MostRecentTU);
private:
- llvm::Expected<PartialTranslationUnit &> ParseOrWrapTopLevelDecl();
+ llvm::Expected<TranslationUnitDecl *> ParseOrWrapTopLevelDecl();
};
} // end namespace clang
diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp
index 7209a33272ef22..039ee4ecc99dd8 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -19,6 +19,7 @@
#include "Wasm.h"
#endif // __EMSCRIPTEN__
+#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Mangle.h"
#include "clang/AST/TypeVisitor.h"
@@ -33,7 +34,10 @@
#include "clang/Driver/Options.h"
#include "clang/Driver/Tool.h"
#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendAction.h"
+#include "clang/Frontend/MultiplexConsumer.h"
#include "clang/Frontend/TextDiagnosticBuffer.h"
+#include "clang/FrontendTool/Utils.h"
#include "clang/Interpreter/Interpreter.h"
#include "clang/Interpreter/Value.h"
#include "clang/Lex/PreprocessorOptions.h"
@@ -50,7 +54,6 @@
#include <cstdarg>
using namespace clang;
-
// FIXME: Figure out how to unify with namespace init_convenience from
// tools/clang-import-test/clang-import-test.cpp
namespace {
@@ -138,6 +141,8 @@ CreateCI(const llvm::opt::ArgStringList &Argv) {
} // anonymous namespace
+namespace clang {
+
llvm::Expected<std::unique_ptr<CompilerInstance>>
IncrementalCompilerBuilder::create(std::string TT,
std::vector<const char *> &ClangArgv) {
@@ -241,20 +246,177 @@ IncrementalCompilerBuilder::CreateCudaHost() {
return IncrementalCompilerBuilder::createCuda(false);
}
+class InProcessPrintingASTConsumer final : public MultiplexConsumer {
+ Interpreter &Interp;
+
+public:
+ InProcessPrintingASTConsumer(std::unique_ptr<ASTConsumer> C, Interpreter &I)
+ : MultiplexConsumer(std::move(C)), Interp(I) {}
+ bool HandleTopLevelDecl(DeclGroupRef DGR) override final {
+ if (DGR.isNull())
+ return true;
+ // if (!Consumer)
+ // return true;
+
+ for (Decl *D : DGR)
+ if (auto *TLSD = llvm::dyn_cast<TopLevelStmtDecl>(D))
+ if (TLSD && TLSD->isSemiMissing()) {
+ auto ExprOrErr =
+ Interp.AttachValuePrinting(cast<Expr>(TLSD->getStmt()));
+ if (llvm::Error E = ExprOrErr.takeError()) {
+ llvm::logAllUnhandledErrors(std::move(E), llvm::errs(),
+ "Value printing failed: ");
+ return false; // abort parsing
+ }
+ TLSD->setStmt(*ExprOrErr);
+ }
+
+ return MultiplexConsumer::HandleTopLevelDecl(DGR);
+ }
+};
+
+/// A custom action enabling the incremental processing functionality.
+///
+/// The usual \p FrontendAction expects one call to ExecuteAction and once it
+/// sees a call to \p EndSourceFile it deletes some of the important objects
+/// such as \p Preprocessor and \p Sema assuming no further input will come.
+///
+/// \p IncrementalAction ensures it keep its underlying action's objects alive
+/// as long as the \p IncrementalParser needs them.
+///
+class IncrementalAction : public WrapperFrontendAction {
+private:
+ bool IsTerminating = false;
+ Interpreter &Interp;
+ std::unique_ptr<ASTConsumer> Consumer;
+
+public:
+ IncrementalAction(CompilerInstance &CI, llvm::LLVMContext &LLVMCtx,
+ llvm::Error &Err, Interpreter &I,
+ std::unique_ptr<ASTConsumer> Consumer = nullptr)
+ : WrapperFrontendAction([&]() {
+ llvm::ErrorAsOutParameter EAO(&Err);
+ std::unique_ptr<FrontendAction> Act;
+ switch (CI.getFrontendOpts().ProgramAction) {
+ default:
+ Err = llvm::createStringError(
+ std::errc::state_not_recoverable,
+ "Driver initialization failed. "
+ "Incremental mode for action %d is not supported",
+ CI.getFrontendOpts().ProgramAction);
+ return Act;
+ case frontend::ASTDump:
+ [[fallthrough]];
+ case frontend::ASTPrint:
+ [[fallthrough]];
+ case frontend::ParseSyntaxOnly:
+ Act = CreateFrontendAction(CI);
+ break;
+ case frontend::PluginAction:
+ [[fallthrough]];
+ case frontend::EmitAssembly:
+ [[fallthrough]];
+ case frontend::EmitBC:
+ [[fallthrough]];
+ case frontend::EmitObj:
+ [[fallthrough]];
+ case frontend::PrintPreprocessedInput:
+ [[fallthrough]];
+ case frontend::EmitLLVMOnly:
+ Act.reset(new EmitLLVMOnlyAction(&LLVMCtx));
+ break;
+ }
+ return Act;
+ }()),
+ Interp(I), Consumer(std::move(Consumer)) {}
+ FrontendAction *getWrapped() const { return WrappedAction.get(); }
+ TranslationUnitKind getTranslationUnitKind() override {
+ return TU_Incremental;
+ }
+
+ std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
+ StringRef InFile) override {
+ std::unique_ptr<ASTConsumer> C =
+ WrapperFrontendAction::CreateASTConsumer(CI, InFile);
+
+ if (Consumer) {
+ std::vector<std::unique_ptr<ASTConsumer>> Cs;
+ Cs.push_back(std::move(Consumer));
+ Cs.push_back(std::move(C));
+ return std::make_unique<MultiplexConsumer>(std::move(Cs));
+ }
+
+ return std::make_unique<InProcessPrintingASTConsumer>(std::move(C), Interp);
+ }
+
+ void ExecuteAction() override {
+ CompilerInstance &CI = getCompilerInstance();
+ assert(CI.hasPreprocessor() && "No PP!");
+
+ // Use a code completion consumer?
+ CodeCompleteConsumer *CompletionConsumer = nullptr;
+ if (CI.hasCodeCompletionConsumer())
+ CompletionConsumer = &CI.getCodeCompletionConsumer();
+
+ Preprocessor &PP = CI.getPreprocessor();
+ PP.EnterMainSourceFile();
+
+ if (!CI.hasSema())
+ CI.createSema(getTranslationUnitKind(), CompletionConsumer);
+ }
+
+ // Do not terminate after processing the input. This allows us to keep various
+ // clang objects alive and to incrementally grow the current TU.
+ void EndSourceFile() override {
+ // The WrappedAction can be nullptr if we issued an error in the ctor.
+ if (IsTerminating && getWrapped())
+ WrapperFrontendAction::EndSourceFile();
+ }
+
+ void FinalizeAction() {
+ assert(!IsTerminating && "Already finalized!");
+ IsTerminating = true;
+ EndSourceFile();
+ }
+};
+
Interpreter::Interpreter(std::unique_ptr<CompilerInstance> CI,
llvm::Error &ErrOut,
- std::unique_ptr<llvm::orc::LLJITBuilder> JITBuilder)
+ std::unique_ptr<llvm::orc::LLJITBuilder> JITBuilder,
+ std::unique_ptr<clang::ASTConsumer> Consumer)
: JITBuilder(std::move(JITBuilder)) {
llvm::ErrorAsOutParameter EAO(&ErrOut);
auto LLVMCtx = std::make_unique<llvm::LLVMContext>();
TSCtx = std::make_unique<llvm::orc::ThreadSafeContext>(std::move(LLVMCtx));
- IncrParser = std::make_unique<IncrementalParser>(
- *this, std::move(CI), *TSCtx->getContext(), ErrOut);
+
+ Act = std::make_unique<IncrementalAction>(*CI, *TSCtx->getContext(), ErrOut,
+ *this, std::move(Consumer));
+ if (ErrOut)
+ return;
+ CI->ExecuteAction(*Act);
+
+ if (getCodeGen())
+ CachedInCodeGenModule = GenModule();
+
+ IncrParser = std::make_unique<IncrementalParser>(std::move(CI), ErrOut);
+
+ // An initial PTU is needed as CUDA includes some headers automatically.
+ auto PTU = Parse("");
+ if (auto E = PTU.takeError()) {
+ ErrOut = joinErrors(std::move(ErrOut), std::move(E));
+ return;
+ }
+
+ if (getCodeGen()) {
+ PTU->TheModule = GenModule();
+ assert(PTU->TheModule && "Failed to create initial PTU");
+ }
+
if (ErrOut)
return;
// Not all frontends support code-generation, e.g. ast-dump actions don't
- if (IncrParser->getCodeGen()) {
+ if (getCodeGen()) {
if (llvm::Error Err = CreateExecutor()) {
ErrOut = joinErrors(std::move(ErrOut), std::move(Err));
return;
@@ -262,7 +424,7 @@ Interpreter::Interpreter(std::unique_ptr<CompilerInstance> CI,
// Process the PTUs that came from initialization. For example -include will
// give us a header that's processed at initialization of the preprocessor.
- for (PartialTranslationUnit &PTU : IncrParser->getPTUs())
+ for (PartialTranslationUnit &PTU : PTUs)
if (llvm::Error Err = Execute(PTU)) {
ErrOut = joinErrors(std::move(ErrOut), std::move(Err));
return;
@@ -271,6 +433,8 @@ Interpreter::Interpreter(std::unique_ptr<CompilerInstance> CI,
}
Interpreter::~Interpreter() {
+ Act->FinalizeAction();
+ IncrParser.reset();
if (IncrExecutor) {
if (llvm::Error Err = IncrExecutor->cleanUp())
llvm::report_fatal_error(
@@ -320,7 +484,6 @@ Interpreter::create(std::unique_ptr<CompilerInstance> CI) {
return PTU.takeError();
Interp->markUserCodeStart();
- Interp->ValuePrintingInfo.resize(4);
return std::move(Interp);
}
@@ -342,8 +505,8 @@ Interpreter::createWithCUDA(std::unique_ptr<CompilerInstance> CI,
llvm::Error Err = llvm::Error::success();
auto DeviceParser = std::make_unique<IncrementalCUDADeviceParser>(
- **Interp, std::move(DCI), *(*Interp)->IncrParser.get(),
- *(*Interp)->TSCtx->getContext(), IMVFS, Err);
+ std::move(DCI), *(*Interp)->IncrParser.get(), IMVFS, Err,
+ (*Interp)->PTUs);
if (Err)
return std::move(Err);
@@ -379,22 +542,21 @@ const ASTContext &Interpreter::getASTContext() const {
void Interpreter::markUserCodeStart() {
assert(!InitPTUSize && "We only do this once");
- InitPTUSize = IncrParser->getPTUs().size();
+ InitPTUSize = PTUs.size();
}
size_t Interpreter::getEffectivePTUSize() const {
- std::list<PartialTranslationUnit> &PTUs = IncrParser->getPTUs();
assert(PTUs.size() >= InitPTUSize && "empty PTU list?");
return PTUs.size() - InitPTUSize;
}
llvm::Expected<PartialTranslationUnit &>
Interpreter::Parse(llvm::StringRef Code) {
- // If we have a device parser, parse it first.
- // The generated code will be included in the host compilation
+ // If we have a device parser, parse it first. The generated code will be
+ // included in the host compilation
if (DeviceParser) {
- auto DevicePTU = DeviceParser->Parse(Code);
- if (auto E = DevicePTU.takeError())
+ llvm::Expected<TranslationUnitDecl *> DeviceTU = DeviceParser->Parse(Code);
+ if (auto E = DeviceTU.takeError())
return std::move(E);
}
@@ -402,7 +564,19 @@ Interpreter::Parse(llvm::StringRef Code) {
// printing could cause it.
getCompilerInstance()->getDiagnostics().setSeverity(
clang::diag::warn_unused_expr, diag::Severity::Ignored, SourceLocation());
- return IncrParser->Parse(Code);
+
+ llvm::Expected<TranslationUnitDecl *> TuOrErr = IncrParser->Parse(Code);
+ if (!TuOrErr)
+ return TuOrErr.takeError();
+
+ PTUs.emplace_back(PartialTranslationUnit());
+ PartialTranslationUnit &LastPTU = PTUs.back();
+ LastPTU.TUPart = *TuOrErr;
+
+ if (std::unique_ptr<llvm::Module> M = GenModule())
+ LastPTU.TheModule = std::move(M);
+
+ return LastPTU;
}
static llvm::Expected<llvm::orc::JITTargetMachineBuilder>
@@ -420,7 +594,7 @@ llvm::Error Interpreter::CreateExecutor() {
return llvm::make_error<llvm::StringError>("Operation failed. "
"Execution engine exists",
std::error_code());
- if (!IncrParser->getCodeGen())
+ if (!getCodeGen())
return llvm::make_error<llvm::StringError>("Operation failed. "
"No code generator available",
std::error_code());
@@ -492,7 +666,7 @@ Interpreter::getSymbolAddress(GlobalDecl GD) const {
return llvm::make_error<llvm::StringError>("Operation failed. "
"No execution engine",
std::error_code());
- llvm::StringRef MangledName = IncrParser->GetMangledName(GD);
+ llvm::StringRef MangledName = getCodeGen()->GetMangledName(GD);
return getSymbolAddress(MangledName);
}
@@ -518,7 +692,6 @@ Interpreter::getSymbolAddressFromLinkerName(llvm::StringRef Name) const {
llvm::Error Interpreter::Undo(unsigned N) {
- std::list<PartialTranslationUnit> &PTUs = IncrParser->getPTUs();
if (N > getEffectivePTUSize())
return llvm::make_error<llvm::StringError>("Operation failed. "
"Too many undos",
@@ -529,7 +702,7 @@ llvm::Error Interpreter::Undo(unsigned N) {
return Err;
}
- IncrParser->CleanUpPTU(PTUs.back());
+ IncrParser->CleanUpPTU(PTUs.back().TUPart);
PTUs.pop_back();
}
return llvm::Error::success();
@@ -551,416 +724,33 @@ llvm::Error Interpreter::LoadDynamicLibrary(const char *name) {
return llvm::Error::success();
}
-llvm::Expected<llvm::orc::ExecutorAddr>
-Interpreter::CompileDtorCall(CXXRecordDecl *CXXRD) {
- assert(CXXRD && "Cannot compile a destructor for a nullptr");
- if (auto Dtor = Dtors.find(CXXRD); Dtor != Dtors.end())
- return Dtor->getSecond();
-
- if (CXXRD->hasIrrelevantDestructor())
- return llvm::orc::ExecutorAddr{};
-
- CXXDestructorDecl *DtorRD =
- getCompilerInstance()->getSema().LookupDestructor(CXXRD);
-
- llvm::StringRef Name =
- IncrParser->GetMangledName(GlobalDecl(DtorRD, Dtor_Base));
- auto AddrOrErr = getSymbolAddress(Name);
- if (!AddrOrErr)
- return AddrOrErr.takeError();
-
- Dtors[CXXRD] = *AddrOrErr;
- return AddrOrErr;
-}
-
-static constexpr llvm::StringRef MagicRuntimeInterface[] = {
- "__clang_Interpreter_SetValueNoAlloc",
- "__clang_Interpreter_SetValueWithAlloc",
- "__clang_Interpreter_SetValueCopyArr", "__ci_newtag"};
-
-static std::unique_ptr<RuntimeInterfaceBuilder>
-createInProcessRuntimeInterfaceBuilder(Interpreter &Interp, ASTContext &Ctx,
- Sema &S);
-
-std::unique_ptr<RuntimeInterfaceBuilder> Interpreter::FindRuntimeInterface() {
- if (llvm::all_of(ValuePrintingInfo, [](Expr *E) { return E != nullptr; }))
- return nullptr;
-
- Sema &S = getCompilerInstance()->getSema();
- ASTContext &Ctx = S.getASTContext();
-
- auto LookupInterface = [&](Expr *&Interface, llvm::StringRef Name) {
- LookupResult R(S, &Ctx.Idents.get(Name), SourceLocation(),
- Sema::LookupOrdinaryName,
- RedeclarationKind::ForVisibleRedeclaration);
- S.LookupQualifiedName(R, Ctx.getTranslationUnitDecl());
- if (R.empty())
- return false;
-
- CXXScopeSpec CSS;
- Interface = S.BuildDeclarationNameExpr(CSS, R, /*ADL=*/false).get();
- return true;
- };
-
- if (!LookupInterface(ValuePrintingInfo[NoAlloc],
- MagicRuntimeInterface[NoAlloc]))
- return nullptr;
- if (Ctx.getLangOpts().CPlusPlus) {
- if (!LookupInterface(ValuePrintingInfo[WithAlloc],
- MagicRuntimeInterface[WithAlloc]))
- return nullptr;
- if (!LookupInterface(ValuePrintingInfo[CopyArray],
- MagicRuntimeInterface[CopyArray]))
- return nullptr;
- if (!LookupInterface(ValuePrintingInfo[NewTag],
- MagicRuntimeInterface[NewTag]))
- return nullptr;
+std::unique_ptr<llvm::Module> Interpreter::GenModule() {
+ static unsigned ID = 0;
+ if (CodeGenerator *CG = getCodeGen()) {
+ // Clang's CodeGen is designed to work with a single llvm::Module. In many
+ // cases for convenience various CodeGen parts have a reference to the
+ // llvm::Module (TheModule or Module) which does not change when a new
+ // module is pushed. However, the execution engine wants to take ownership
+ // of the module which does not map well to CodeGen's design. To work this
+ // around we created an empty module to make CodeGen happy. We should make
+ // sure it always stays empty.
+ assert((!CachedInCodeGenModule || (CachedInCodeGenModule->empty() &&
+ CachedInCodeGenModule->global_empty() &&
+ CachedInCodeGenModule->alias_empty() &&
+ CachedInCodeGenModule->ifunc_empty())) &&
+ "CodeGen wrote to a readonly module");
+ std::unique_ptr<llvm::Module> M(CG->ReleaseModule());
+ CG->StartModule("incr_module_" + std::to_string(ID++), M->getContext());
+ return M;
}
-
- return createInProcessRuntimeInterfaceBuilder(*this, Ctx, S);
+ return nullptr;
}
-namespace {
-
-class InterfaceKindVisitor
- : public TypeVisitor<InterfaceKindVisitor, Interpreter::InterfaceKind> {
- friend class InProcessRuntimeInterfaceBuilder;
-
- ASTContext &Ctx;
- Sema &S;
- Expr *E;
- llvm::SmallVector<Expr *, 3> Args;
-
-public:
- InterfaceKindVisitor(ASTContext &Ctx, Sema &S, Expr *E)
- : Ctx(Ctx), S(S), E(E) {}
-
- Interpreter::InterfaceKind VisitRecordType(const RecordType *Ty) {
- return Interpreter::InterfaceKind::WithAlloc;
- }
-
- Interpreter::InterfaceKind
- VisitMemberPointerType(const MemberPointerType *Ty) {
- return Interpreter::InterfaceKind::WithAlloc;
- }
-
- Interpreter::InterfaceKind
- VisitConstantArrayType(const ConstantArrayType *Ty) {
- return Interpreter::InterfaceKind::CopyArray;
- }
-
- Interpreter::InterfaceKind
- VisitFunctionProtoType(const FunctionProtoType *Ty) {
- HandlePtrType(Ty);
- return Interpreter::InterfaceKind::NoAlloc;
- }
-
- Interpreter::InterfaceKind VisitPointerType(const PointerType *Ty) {
- HandlePtrType(Ty);
- return Interpreter::InterfaceKind::NoAlloc;
- }
-
- Interpreter::InterfaceKind VisitReferenceType(const ReferenceType *Ty) {
- ExprResult AddrOfE = S.CreateBuiltinUnaryOp(SourceLocation(), UO_AddrOf, E);
- assert(!AddrOfE.isInvalid() && "Can not create unary expression");
- Args.push_back(AddrOfE.get());
- return Interpreter::InterfaceKind::NoAlloc;
- }
-
- Interpreter::InterfaceKind VisitBuiltinType(const BuiltinType *Ty) {
- if (Ty->isNullPtrType())
- Args.push_back(E);
- else if (Ty->isFloatingType())
- Args.push_back(E);
- else if (Ty->isIntegralOrEnumerationType())
- HandleIntegralOrEnumType(Ty);
- else if (Ty->isVoidType()) {
- // Do we need to still run `E`?
- }
-
- return Interpreter::InterfaceKind::NoAlloc;
- }
-
- Interpreter::InterfaceKind VisitEnumType(const EnumType *Ty) {
- HandleIntegralOrEnumType(Ty);
- return Interpreter::InterfaceKind::NoAlloc;
- }
-
-private:
- // Force cast these types to the uint that fits the register size. That way we
- // reduce the number of overloads of `__clang_Interpreter_SetValueNoAlloc`.
- void HandleIntegralOrEnumType(const Type *Ty) {
- uint64_t PtrBits = Ctx.getTypeSize(Ctx.VoidPtrTy);
- QualType UIntTy = Ctx.getBitIntType(/*Unsigned=*/true, PtrBits);
- TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(UIntTy);
- ExprResult CastedExpr =
- S.BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), E);
- assert(!CastedExpr.isInvalid() && "Cannot create cstyle cast expr");
- Args.push_back(CastedExpr.get());
- }
-
- void HandlePtrType(const Type *Ty) {
- TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ctx.VoidPtrTy);
- ExprResult CastedExpr =
- S.BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), E);
- assert(!CastedExpr.isInvalid() && "Can not create cstyle cast expression");
- Args.push_back(CastedExpr.get());
- }
-};
-
-class InProcessRuntimeInterfaceBuilder : public RuntimeInterfaceBuilder {
- Interpreter &Interp;
- ASTContext &Ctx;
- Sema &S;
-
-public:
- InProcessRuntimeInterfaceBuilder(Interpreter &Interp, ASTContext &C, Sema &S)
- : Interp(Interp), Ctx(C), S(S) {}
-
- TransformExprFunction *getPrintValueTransformer() override {
- return &transformForValuePrinting;
- }
-
-private:
- static ExprResult transformForValuePrinting(RuntimeInterfaceBuilder *Builder,
- Expr *E,
- ArrayRef<Expr *> FixedArgs) {
- auto *B = static_cast<InProcessRuntimeInterfaceBuilder *>(Builder);
-
- // Get rid of ExprWithCleanups.
- if (auto *EWC = llvm::dyn_cast_if_present<ExprWithCleanups>(E))
- E = EWC->getSubExpr();
-
- InterfaceKindVisitor Visitor(B->Ctx, B->S, E);
-
- // The Interpreter* parameter and the out parameter `OutVal`.
- for (Expr *E : FixedArgs)
- Visitor.Args.push_back(E);
-
- QualType Ty = E->getType();
- QualType DesugaredTy = Ty.getDesugaredType(B->Ctx);
-
- // For lvalue struct, we treat it as a reference.
- if (DesugaredTy->isRecordType() && E->isLValue()) {
- DesugaredTy = B->Ctx.getLValueReferenceType(DesugaredTy);
- Ty = B->Ctx.getLValueReferenceType(Ty);
- }
-
- Expr *TypeArg = CStyleCastPtrExpr(B->S, B->Ctx.VoidPtrTy,
- (uintptr_t)Ty.getAsOpaquePtr());
- // The QualType parameter `OpaqueType`, represented as `void*`.
- Visitor.Args.push_back(TypeArg);
-
- // We push the last parameter based on the type of the Expr. Note we need
- // special care for rvalue struct.
- Interpreter::InterfaceKind Kind = Visitor.Visit(&*DesugaredTy);
- switch (Kind) {
- case Interpreter::InterfaceKind::WithAlloc:
- case Interpreter::InterfaceKind::CopyArray: {
- // __clang_Interpreter_SetValueWithAlloc.
- ExprResult AllocCall = B->S.ActOnCallExpr(
- /*Scope=*/nullptr,
- B->Interp
- .getValuePrintingInfo()[Interpreter::InterfaceKind::WithAlloc],
- E->getBeginLoc(), Visitor.Args, E->getEndLoc());
- assert(!AllocCall.isInvalid() && "Can't create runtime interface call!");
-
- TypeSourceInfo *TSI =
- B->Ctx.getTrivialTypeSourceInfo(Ty, SourceLocation());
-
- // Force CodeGen to emit destructor.
- if (auto *RD = Ty->getAsCXXRecordDecl()) {
- auto *Dtor = B->S.LookupDestructor(RD);
- Dtor->addAttr(UsedAttr::CreateImplicit(B->Ctx));
- B->Interp.getCompilerInstance()->getASTConsumer().HandleTopLevelDecl(
- DeclGroupRef(Dtor));
- }
-
- // __clang_Interpreter_SetValueCopyArr.
- if (Kind == Interpreter::InterfaceKind::CopyArray) {
- const auto *ConstantArrTy =
- cast<ConstantArrayType>(DesugaredTy.getTypePtr());
- size_t ArrSize = B->Ctx.getConstantArrayElementCount(ConstantArrTy);
- Expr *ArrSizeExpr = IntegerLiteralExpr(B->Ctx, ArrSize);
- Expr *Args[] = {E, AllocCall.get(), ArrSizeExpr};
- return B->S.ActOnCallExpr(
- /*Scope *=*/nullptr,
- B->Interp
- .getValuePrintingInfo()[Interpreter::InterfaceKind::CopyArray],
- SourceLocation(), Args, SourceLocation());
- }
- Expr *Args[] = {
- AllocCall.get(),
- B->Interp.getValuePrintingInfo()[Interpreter::InterfaceKind::NewTag]};
- ExprResult CXXNewCall = B->S.BuildCXXNew(
- E->getSourceRange(),
- /*UseGlobal=*/true, /*PlacementLParen=*/SourceLocation(), Args,
- /*PlacementRParen=*/SourceLocation(),
- /*TypeIdParens=*/SourceRange(), TSI->getType(), TSI, std::nullopt,
- E->getSourceRange(), E);
-
- assert(!CXXNewCall.isInvalid() &&
- "Can't create runtime placement new call!");
-
- return B->S.ActOnFinishFullExpr(CXXNewCall.get(),
- /*DiscardedValue=*/false);
- }
- // __clang_Interpreter_SetValueNoAlloc.
- case Interpreter::InterfaceKind::NoAlloc: {
- return B->S.ActOnCallExpr(
- /*Scope=*/nullptr,
- B->Interp.getValuePrintingInfo()[Interpreter::InterfaceKind::NoAlloc],
- E->getBeginLoc(), Visitor.Args, E->getEndLoc());
- }
- default:
- llvm_unreachable("Unhandled Interpreter::InterfaceKind");
- }
- }
-};
-} // namespace
-
-static std::unique_ptr<RuntimeInterfaceBuilder>
-createInProcessRuntimeInterfaceBuilder(Interpreter &Interp, ASTContext &Ctx,
- Sema &S) {
- return std::make_unique<InProcessRuntimeInterfaceBuilder>(Interp, Ctx, S);
-}
-
-// This synthesizes a call expression to a speciall
-// function that is responsible for generating the Value.
-// In general, we transform:
-// clang-repl> x
-// To:
-// // 1. If x is a built-in type like int, float.
-// __clang_Interpreter_SetValueNoAlloc(ThisInterp, OpaqueValue, xQualType, x);
-// // 2. If x is a struct, and a lvalue.
-// __clang_Interpreter_SetValueNoAlloc(ThisInterp, OpaqueValue, xQualType,
-// &x);
-// // 3. If x is a struct, but a rvalue.
-// new (__clang_Interpreter_SetValueWithAlloc(ThisInterp, OpaqueValue,
-// xQualType)) (x);
-
-Expr *Interpreter::SynthesizeExpr(Expr *E) {
- Sema &S = getCompilerInstance()->getSema();
- ASTContext &Ctx = S.getASTContext();
-
- if (!RuntimeIB) {
- RuntimeIB = FindRuntimeInterface();
- AddPrintValueCall = RuntimeIB->getPrintValueTransformer();
- }
-
- assert(AddPrintValueCall &&
- "We don't have a runtime interface for pretty print!");
-
- // Create parameter `ThisInterp`.
- auto *ThisInterp = CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)this);
-
- // Create parameter `OutVal`.
- auto *OutValue = CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)&LastValue);
-
- // Build `__clang_Interpreter_SetValue*` call.
- ExprResult Result =
- AddPrintValueCall(RuntimeIB.get(), E, {ThisInterp, OutValue});
-
- // It could fail, like printing an array type in C. (not supported)
- if (Result.isInvalid())
- return E;
- return Result.get();
-}
-
-// Temporary rvalue struct that need special care.
-REPL_EXTERNAL_VISIBILITY void *
-__clang_Interpreter_SetValueWithAlloc(void *This, void *OutVal,
- void *OpaqueType) {
- Value &VRef = *(Value *)OutVal;
- VRef = Value(static_cast<Interpreter *>(This), OpaqueType);
- return VRef.getPtr();
-}
-
-extern "C" void REPL_EXTERNAL_VISIBILITY __clang_Interpreter_SetValueNoAlloc(
- void *This, void *OutVal, void *OpaqueType, ...) {
- Value &VRef = *(Value *)OutVal;
- Interpreter *I = static_cast<Interpreter *>(This);
- VRef = Value(I, OpaqueType);
- if (VRef.isVoid())
- return;
-
- va_list args;
- va_start(args, /*last named param*/ OpaqueType);
-
- QualType QT = VRef.getType();
- if (VRef.getKind() == Value::K_PtrOrObj) {
- VRef.setPtr(va_arg(args, void *));
- } else {
- if (const auto *ET = QT->getAs<EnumType>())
- QT = ET->getDecl()->getIntegerType();
- switch (QT->castAs<BuiltinType>()->getKind()) {
- default:
- llvm_unreachable("unknown type kind!");
- break;
- // Types shorter than int are resolved as int, else va_arg has UB.
- case BuiltinType::Bool:
- VRef.setBool(va_arg(args, int));
- break;
- case BuiltinType::Char_S:
- VRef.setChar_S(va_arg(args, int));
- break;
- case BuiltinType::SChar:
- VRef.setSChar(va_arg(args, int));
- break;
- case BuiltinType::Char_U:
- VRef.setChar_U(va_arg(args, unsigned));
- break;
- case BuiltinType::UChar:
- VRef.setUChar(va_arg(args, unsigned));
- break;
- case BuiltinType::Short:
- VRef.setShort(va_arg(args, int));
- break;
- case BuiltinType::UShort:
- VRef.setUShort(va_arg(args, unsigned));
- break;
- case BuiltinType::Int:
- VRef.setInt(va_arg(args, int));
- break;
- case BuiltinType::UInt:
- VRef.setUInt(va_arg(args, unsigned));
- break;
- case BuiltinType::Long:
- VRef.setLong(va_arg(args, long));
- break;
- case BuiltinType::ULong:
- VRef.setULong(va_arg(args, unsigned long));
- break;
- case BuiltinType::LongLong:
- VRef.setLongLong(va_arg(args, long long));
- break;
- case BuiltinType::ULongLong:
- VRef.setULongLong(va_arg(args, unsigned long long));
- break;
- // Types shorter than double are resolved as double, else va_arg has UB.
- case BuiltinType::Float:
- VRef.setFloat(va_arg(args, double));
- break;
- case BuiltinType::Double:
- VRef.setDouble(va_arg(args, double));
- break;
- case BuiltinType::LongDouble:
- VRef.setLongDouble(va_arg(args, long double));
- break;
- // See REPL_BUILTIN_TYPES.
- }
- }
- va_end(args);
+CodeGenerator *Interpreter::getCodeGen() const {
+ FrontendAction *WrappedAct = Act->getWrapped();
+ if (!WrappedAct->hasIRSupport())
+ return nullptr;
+ return static_cast<CodeGenAction *>(WrappedAct)->getCodeGenerator();
}
-// A trampoline to work around the fact that operator placement new cannot
-// really be forward declared due to libc++ and libstdc++ declaration mismatch.
-// FIXME: __clang_Interpreter_NewTag is ODR violation because we get the same
-// definition in the interpreter runtime. We should move it in a runtime header
-// which gets included by the interpreter and here.
-struct __clang_Interpreter_NewTag {};
-REPL_EXTERNAL_VISIBILITY void *
-operator new(size_t __sz, void *__p, __clang_Interpreter_NewTag) noexcept {
- // Just forward to the standard operator placement new.
- return operator new(__sz, __p);
-}
+} // end namespace clang
diff --git a/clang/lib/Interpreter/InterpreterUtils.cpp b/clang/lib/Interpreter/InterpreterUtils.cpp
index 45f6322b8461ed..2cfbb2fcb45063 100644
--- a/clang/lib/Interpreter/InterpreterUtils.cpp
+++ b/clang/lib/Interpreter/InterpreterUtils.cpp
@@ -81,7 +81,7 @@ NamedDecl *LookupNamed(Sema &S, llvm::StringRef Name,
else {
const DeclContext *PrimaryWithin = nullptr;
if (const auto *TD = dyn_cast<TagDecl>(Within))
- PrimaryWithin = llvm::dyn_cast_or_null<DeclContext>(TD->getDefinition());
+ PrimaryWithin = dyn_cast_if_present<DeclContext>(TD->getDefinition());
else
PrimaryWithin = Within->getPrimaryContext();
@@ -97,15 +97,404 @@ NamedDecl *LookupNamed(Sema &S, llvm::StringRef Name,
R.resolveKind();
if (R.isSingleResult())
- return llvm::dyn_cast<NamedDecl>(R.getFoundDecl());
+ return dyn_cast<NamedDecl>(R.getFoundDecl());
return nullptr;
}
+static NestedNameSpecifier *CreateOuterNNS(const ASTContext &Ctx, const Decl *D,
+ bool FullyQualify) {
+ const DeclContext *DC = D->getDeclContext();
+ if (const auto *NS = dyn_cast<NamespaceDecl>(DC)) {
+ while (NS && NS->isInline()) {
+ // Ignore inline namespace;
+ NS = dyn_cast_if_present<NamespaceDecl>(NS->getDeclContext());
+ }
+ if (NS && NS->getDeclName())
+ return CreateNestedNameSpecifier(Ctx, NS);
+ return nullptr; // no starting '::', no anonymous
+ }
+ if (const auto *TD = dyn_cast<TagDecl>(DC))
+ return CreateNestedNameSpecifier(Ctx, TD, FullyQualify);
+ if (const auto *TDD = dyn_cast<TypedefNameDecl>(DC))
+ return CreateNestedNameSpecifier(Ctx, TDD, FullyQualify);
+ return nullptr; // no starting '::'
+}
+
+static NestedNameSpecifier *
+CreateNestedNameSpecifierForScopeOf(const ASTContext &Ctx, const Decl *D,
+ bool FullyQualified) {
+ // Create a nested name specifier for the declaring context of the type.
+
+ assert(D);
+
+ const auto *Outer = dyn_cast_if_present<NamedDecl>(D->getDeclContext());
+ const auto *OuterNs = dyn_cast_if_present<NamespaceDecl>(D->getDeclContext());
+ if (Outer && !(OuterNs && OuterNs->isAnonymousNamespace())) {
+
+ if (const auto *CXXD = dyn_cast<CXXRecordDecl>(D->getDeclContext())) {
+
+ if (ClassTemplateDecl *CTD = CXXD->getDescribedClassTemplate()) {
+ // We are in the case of a type(def) that was declared in a
+ // class template but is *not* type dependent. In clang, it gets
+ // attached to the class template declaration rather than any
+ // specific class template instantiation. This result in 'odd'
+ // fully qualified typename:
+ // vector<_Tp,_Alloc>::size_type
+ // Make the situation is 'useable' but looking a bit odd by
+ // picking a random instance as the declaring context.
+ // FIXME: We should not use the iterators here to check if we are in
+ // a template specialization. clTempl != cxxdecl already tell us that
+ // is the case. It seems that we rely on a side-effect from triggering
+ // deserializations to support 'some' use-case. See ROOT-9709.
+ if (CTD->spec_begin() != CTD->spec_end()) {
+ D = *(CTD->spec_begin());
+ Outer = dyn_cast<NamedDecl>(D);
+ OuterNs = dyn_cast<NamespaceDecl>(D);
+ }
+ }
+ }
+
+ if (OuterNs)
+ return CreateNestedNameSpecifier(Ctx, OuterNs);
+
+ if (const auto *TD = dyn_cast<TagDecl>(Outer))
+ return CreateNestedNameSpecifier(Ctx, TD, FullyQualified);
+ }
+ return nullptr;
+}
+
+static NestedNameSpecifier *
+CreateNestedNameSpecifierForScopeOf(const ASTContext &Ctx, const Type *TypePtr,
+ bool FullyQualified) {
+ // Create a nested name specifier for the declaring context of the type.
+
+ if (!TypePtr)
+ return nullptr;
+
+ Decl *D = nullptr;
+ if (const auto *TDT = dyn_cast<TypedefType>(TypePtr)) {
+ D = TDT->getDecl();
+ } else {
+ // There are probably other cases ...
+ if (const auto *TT = dyn_cast_if_present<TagType>(TypePtr))
+ D = TT->getDecl();
+ else
+ D = TypePtr->getAsCXXRecordDecl();
+ }
+
+ if (!D)
+ return nullptr;
+
+ return CreateNestedNameSpecifierForScopeOf(Ctx, D, FullyQualified);
+}
+
+static NestedNameSpecifier *
+GetFullyQualifiedNameSpecifier(const ASTContext &Ctx,
+ NestedNameSpecifier *Scope) {
+ // Return a fully qualified version of this name specifier
+ if (Scope->getKind() == NestedNameSpecifier::Global) {
+ // Already fully qualified.
+ return Scope;
+ }
+
+ if (const Type *Type = Scope->getAsType()) {
+ // Find decl context.
+ const TagDecl *TD = nullptr;
+ if (const auto *TT = dyn_cast<TagType>(Type))
+ TD = TT->getDecl();
+ else
+ TD = Type->getAsCXXRecordDecl();
+
+ if (TD)
+ return CreateNestedNameSpecifier(Ctx, TD, true /*FullyQualified*/);
+
+ if (const auto *TDD = dyn_cast<TypedefType>(Type))
+ return CreateNestedNameSpecifier(Ctx, TDD->getDecl(),
+ true /*FullyQualified*/);
+ } else if (const NamespaceDecl *NS = Scope->getAsNamespace())
+ return CreateNestedNameSpecifier(Ctx, NS);
+ else if (const auto *Alias = Scope->getAsNamespaceAlias())
+ return CreateNestedNameSpecifier(Ctx,
+ Alias->getNamespace()->getCanonicalDecl());
+
+ return Scope;
+}
+
+static bool GetFullyQualifiedTemplateName(const ASTContext &Ctx,
+ TemplateName &Name) {
+
+ bool Changed = false;
+ NestedNameSpecifier *NNS = nullptr;
+
+ TemplateDecl *TD = Name.getAsTemplateDecl();
+ QualifiedTemplateName *QTN = Name.getAsQualifiedTemplateName();
+
+ if (QTN && !QTN->hasTemplateKeyword()) {
+ NNS = QTN->getQualifier();
+ NestedNameSpecifier *QNNS = GetFullyQualifiedNameSpecifier(Ctx, NNS);
+ if (QNNS != NNS) {
+ Changed = true;
+ NNS = QNNS;
+ } else {
+ NNS = nullptr;
+ }
+ } else {
+ NNS = CreateNestedNameSpecifierForScopeOf(Ctx, TD, true);
+ }
+ if (NNS) {
+ Name = Ctx.getQualifiedTemplateName(NNS,
+ /*TemplateKeyword=*/false,
+ TemplateName(TD));
+ Changed = true;
+ }
+ return Changed;
+}
+
+static bool GetFullyQualifiedTemplateArgument(const ASTContext &Ctx,
+ TemplateArgument &Arg) {
+ bool Changed = false;
+
+ // Note: we do not handle TemplateArgument::Expression, to replace it
+ // we need the information for the template instance decl.
+ // See GetPartiallyDesugaredTypeImpl
+
+ if (Arg.getKind() == TemplateArgument::Template) {
+ TemplateName Name = Arg.getAsTemplate();
+ Changed = GetFullyQualifiedTemplateName(Ctx, Name);
+ if (Changed) {
+ Arg = TemplateArgument(Name);
+ }
+ } else if (Arg.getKind() == TemplateArgument::Type) {
+ QualType SubTy = Arg.getAsType();
+ // Check if the type needs more desugaring and recurse.
+ QualType QTFQ = GetFullyQualifiedType(SubTy, Ctx);
+ if (QTFQ != SubTy) {
+ Arg = TemplateArgument(QTFQ);
+ Changed = true;
+ }
+ } else if (Arg.getKind() == TemplateArgument::Pack) {
+ SmallVector<TemplateArgument, 2> desArgs;
+ for (auto I = Arg.pack_begin(), E = Arg.pack_end(); I != E; ++I) {
+ TemplateArgument PackArg(*I);
+ Changed = GetFullyQualifiedTemplateArgument(Ctx, PackArg);
+ desArgs.push_back(PackArg);
+ }
+ if (Changed) {
+ // The allocator in ASTContext is mutable ...
+ // Keep the argument const to be inline will all the other interfaces
+ // like: NestedNameSpecifier::Create
+ ASTContext &MutableCtx(const_cast<ASTContext &>(Ctx));
+ Arg = TemplateArgument::CreatePackCopy(MutableCtx, desArgs);
+ }
+ }
+ return Changed;
+}
+
+static const Type *GetFullyQualifiedLocalType(const ASTContext &Ctx,
+ const Type *Typeptr) {
+ // We really just want to handle the template parameter if any ....
+ // In case of template specializations iterate over the arguments and
+ // fully qualify them as well.
+ if (const auto *TST = dyn_cast<const TemplateSpecializationType>(Typeptr)) {
+
+ bool MightHaveChanged = false;
+ llvm::SmallVector<TemplateArgument, 4> DesArgs;
+ for (auto &I : TST->template_arguments()) {
+
+ // cheap to copy and potentially modified by
+ // GetFullyQualifedTemplateArgument
+ TemplateArgument Arg(I);
+ MightHaveChanged |= GetFullyQualifiedTemplateArgument(Ctx, Arg);
+ DesArgs.push_back(Arg);
+ }
+
+ // If desugaring happened allocate new type in the AST.
+ if (MightHaveChanged) {
+ QualType QT = Ctx.getTemplateSpecializationType(
+ TST->getTemplateName(), DesArgs, TST->getCanonicalTypeInternal());
+ return QT.getTypePtr();
+ }
+ } else if (const auto *TSTRecord = dyn_cast<const RecordType>(Typeptr)) {
+ // We are asked to fully qualify and we have a Record Type,
+ // which can point to a template instantiation with no sugar in any of
+ // its template argument, however we still need to fully qualify them.
+
+ if (const auto *TSTdecl =
+ dyn_cast<ClassTemplateSpecializationDecl>(TSTRecord->getDecl())) {
+ const TemplateArgumentList &TemplateArgs = TSTdecl->getTemplateArgs();
+
+ bool MightHaveChanged = false;
+ llvm::SmallVector<TemplateArgument, 4> DesArgs;
+ for (unsigned int I = 0, E = TemplateArgs.size(); I != E; ++I) {
+
+ // cheap to copy and potentially modified by
+ // GetFullyQualifedTemplateArgument
+ TemplateArgument Arg(TemplateArgs[I]);
+ MightHaveChanged |= GetFullyQualifiedTemplateArgument(Ctx, Arg);
+ DesArgs.push_back(Arg);
+ }
+
+ // If desugaring happened allocate new type in the AST.
+ if (MightHaveChanged) {
+ TemplateName TN(TSTdecl->getSpecializedTemplate());
+ QualType QT = Ctx.getTemplateSpecializationType(
+ TN, DesArgs, TSTRecord->getCanonicalTypeInternal());
+ return QT.getTypePtr();
+ }
+ }
+ }
+ return Typeptr;
+}
+
+NestedNameSpecifier *CreateNestedNameSpecifier(const ASTContext &Ctx,
+ const NamespaceDecl *NSD) {
+ while (NSD && NSD->isInline()) {
+ // Ignore inline namespace;
+ NSD = dyn_cast_if_present<NamespaceDecl>(NSD->getDeclContext());
+ }
+ if (!NSD)
+ return nullptr;
+
+ bool FullyQualified = true; // doesn't matter, DeclContexts are namespaces
+ return NestedNameSpecifier::Create(
+ Ctx, CreateOuterNNS(Ctx, NSD, FullyQualified), NSD);
+}
+
+NestedNameSpecifier *CreateNestedNameSpecifier(const ASTContext &Ctx,
+ const TypedefNameDecl *TD,
+ bool FullyQualify) {
+ return NestedNameSpecifier::Create(Ctx, CreateOuterNNS(Ctx, TD, FullyQualify),
+ /*Template=*/true, TD->getTypeForDecl());
+}
+
+NestedNameSpecifier *CreateNestedNameSpecifier(const ASTContext &Ctx,
+ const TagDecl *TD,
+ bool FullyQualify) {
+ const Type *Ty = Ctx.getTypeDeclType(TD).getTypePtr();
+ if (FullyQualify)
+ Ty = GetFullyQualifiedLocalType(Ctx, Ty);
+ return NestedNameSpecifier::Create(Ctx, CreateOuterNNS(Ctx, TD, FullyQualify),
+ /*Template=*/false, Ty);
+}
+
+QualType GetFullyQualifiedType(QualType QT, const ASTContext &Ctx) {
+ // Return the fully qualified type, if we need to recurse through any
+ // template parameter, this needs to be merged somehow with
+ // GetPartialDesugaredType.
+
+ // In case of myType* we need to strip the pointer first, fully qualifiy
+ // and attach the pointer once again.
+ if (isa<PointerType>(QT.getTypePtr())) {
+ // Get the qualifiers.
+ Qualifiers Quals = QT.getQualifiers();
+ QT = GetFullyQualifiedType(QT->getPointeeType(), Ctx);
+ QT = Ctx.getPointerType(QT);
+ // Add back the qualifiers.
+ QT = Ctx.getQualifiedType(QT, Quals);
+ return QT;
+ }
+
+ // In case of myType& we need to strip the pointer first, fully qualifiy
+ // and attach the pointer once again.
+ if (isa<ReferenceType>(QT.getTypePtr())) {
+ // Get the qualifiers.
+ bool IsLValueRefTy = isa<LValueReferenceType>(QT.getTypePtr());
+ Qualifiers Quals = QT.getQualifiers();
+ QT = GetFullyQualifiedType(QT->getPointeeType(), Ctx);
+ // Add the r- or l-value reference type back to the desugared one.
+ if (IsLValueRefTy)
+ QT = Ctx.getLValueReferenceType(QT);
+ else
+ QT = Ctx.getRValueReferenceType(QT);
+ // Add back the qualifiers.
+ QT = Ctx.getQualifiedType(QT, Quals);
+ return QT;
+ }
+
+ // Strip deduced types.
+ if (const auto *AutoTy = dyn_cast<AutoType>(QT.getTypePtr())) {
+ if (!AutoTy->getDeducedType().isNull())
+ return GetFullyQualifiedType(
+ AutoTy->getDeducedType().getDesugaredType(Ctx), Ctx);
+ }
+
+ // Remove the part of the type related to the type being a template
+ // parameter (we won't report it as part of the 'type name' and it is
+ // actually make the code below to be more complex (to handle those)
+ while (isa<SubstTemplateTypeParmType>(QT.getTypePtr())) {
+ // Get the qualifiers.
+ Qualifiers Quals = QT.getQualifiers();
+
+ QT = cast<SubstTemplateTypeParmType>(QT.getTypePtr())->desugar();
+
+ // Add back the qualifiers.
+ QT = Ctx.getQualifiedType(QT, Quals);
+ }
+
+ NestedNameSpecifier *Prefix = nullptr;
+ Qualifiers PrefixQualifiers;
+ if (const auto *EType = dyn_cast<ElaboratedType>(QT.getTypePtr())) {
+ // Intentionally, we do not care about the other compononent of
+ // the elaborated type (the keyword) as part of the partial
+ // desugaring (and/or name normalization) is to remove it.
+ Prefix = EType->getQualifier();
+ if (Prefix) {
+ const NamespaceDecl *NS = Prefix->getAsNamespace();
+ if (Prefix != NestedNameSpecifier::GlobalSpecifier(Ctx) &&
+ !(NS && NS->isAnonymousNamespace())) {
+ PrefixQualifiers = QT.getLocalQualifiers();
+ Prefix = GetFullyQualifiedNameSpecifier(Ctx, Prefix);
+ QT = QualType(EType->getNamedType().getTypePtr(), 0);
+ } else {
+ Prefix = nullptr;
+ }
+ }
+ } else {
+
+ // Create a nested name specifier if needed (i.e. if the decl context
+ // is not the global scope.
+ Prefix = CreateNestedNameSpecifierForScopeOf(Ctx, QT.getTypePtr(),
+ true /*FullyQualified*/);
+
+ // move the qualifiers on the outer type (avoid 'std::const string'!)
+ if (Prefix) {
+ PrefixQualifiers = QT.getLocalQualifiers();
+ QT = QualType(QT.getTypePtr(), 0);
+ }
+ }
+
+ // In case of template specializations iterate over the arguments and
+ // fully qualify them as well.
+ if (isa<const TemplateSpecializationType>(QT.getTypePtr())) {
+
+ Qualifiers Qualifiers = QT.getLocalQualifiers();
+ const Type *TypePtr = GetFullyQualifiedLocalType(Ctx, QT.getTypePtr());
+ QT = Ctx.getQualifiedType(TypePtr, Qualifiers);
+
+ } else if (isa<const RecordType>(QT.getTypePtr())) {
+ // We are asked to fully qualify and we have a Record Type,
+ // which can point to a template instantiation with no sugar in any of
+ // its template argument, however we still need to fully qualify them.
+
+ Qualifiers Qualifiers = QT.getLocalQualifiers();
+ const Type *TypePtr = GetFullyQualifiedLocalType(Ctx, QT.getTypePtr());
+ QT = Ctx.getQualifiedType(TypePtr, Qualifiers);
+ }
+ if (Prefix) {
+ // We intentionally always use ElaboratedTypeKeyword::None, we never want
+ // the keyword (humm ... what about anonymous types?)
+ QT = Ctx.getElaboratedType(ElaboratedTypeKeyword::None, Prefix, QT);
+ QT = Ctx.getQualifiedType(QT, PrefixQualifiers);
+ }
+ return QT;
+}
+
std::string GetFullTypeName(ASTContext &Ctx, QualType QT) {
+ QualType FQT = GetFullyQualifiedType(QT, Ctx);
PrintingPolicy Policy(Ctx.getPrintingPolicy());
Policy.SuppressScope = false;
Policy.AnonymousTagLocations = false;
- return QT.getAsString(Policy);
+ return FQT.getAsString(Policy);
}
} // namespace clang
diff --git a/clang/lib/Interpreter/InterpreterUtils.h b/clang/lib/Interpreter/InterpreterUtils.h
index c7b405b486d936..4b92cf0c4bf978 100644
--- a/clang/lib/Interpreter/InterpreterUtils.h
+++ b/clang/lib/Interpreter/InterpreterUtils.h
@@ -47,7 +47,25 @@ NamespaceDecl *LookupNamespace(Sema &S, llvm::StringRef Name,
NamedDecl *LookupNamed(Sema &S, llvm::StringRef Name,
const DeclContext *Within);
+NestedNameSpecifier *CreateNestedNameSpecifier(const ASTContext &Ctx,
+ const NamespaceDecl *Namesp);
+
+NestedNameSpecifier *CreateNestedNameSpecifier(const ASTContext &Ctx,
+ const TypedefNameDecl *TD,
+ bool FullyQualify);
+
+NestedNameSpecifier *CreateNestedNameSpecifier(const ASTContext &Ctx,
+ const TagDecl *TD,
+ bool FullyQualify);
+
+QualType GetFullyQualifiedType(QualType QT, const ASTContext &Ctx);
+
std::string GetFullTypeName(ASTContext &Ctx, QualType QT);
+
+class Value;
+
+std::string ReplPrintTypeImpl(const Value &);
+std::string ReplPrintDataImpl(const Value &);
} // namespace clang
#endif
diff --git a/clang/lib/Interpreter/InterpreterValuePrinter.cpp b/clang/lib/Interpreter/InterpreterValuePrinter.cpp
new file mode 100644
index 00000000000000..a622d681a96f84
--- /dev/null
+++ b/clang/lib/Interpreter/InterpreterValuePrinter.cpp
@@ -0,0 +1,890 @@
+//===--- InterpreterValuePrinter.cpp - Value printing utils -----*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements routines for in-process value printing in clang-repl.
+//
+//===----------------------------------------------------------------------===//
+
+#include "IncrementalParser.h"
+#include "InterpreterUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/PrettyPrinter.h"
+#include "clang/AST/Type.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Interpreter/Interpreter.h"
+#include "clang/Interpreter/Value.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Sema/Lookup.h"
+#include "clang/Sema/Sema.h"
+
+#include "llvm/Support/Error.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <cassert>
+#include <string>
+
+using namespace clang;
+
+static std::string PrintDeclType(const QualType &QT, NamedDecl *D) {
+ std::string Str;
+ llvm::raw_string_ostream SS(Str);
+ if (QT.hasQualifiers())
+ SS << QT.getQualifiers().getAsString() << " ";
+ SS << D->getQualifiedNameAsString();
+ return Str;
+}
+
+static std::string PrintQualType(ASTContext &Ctx, QualType QT) {
+ PrintingPolicy Policy(Ctx.getPrintingPolicy());
+ // Print the Allocator in STL containers, for instance.
+ Policy.SuppressDefaultTemplateArgs = false;
+ Policy.SuppressUnwrittenScope = true;
+ // Print 'a<b<c> >' rather than 'a<b<c>>'.
+ Policy.SplitTemplateClosers = true;
+
+ struct LocalPrintingPolicyRAII {
+ ASTContext &Context;
+ PrintingPolicy Policy;
+
+ LocalPrintingPolicyRAII(ASTContext &Ctx, PrintingPolicy &PP)
+ : Context(Ctx), Policy(Ctx.getPrintingPolicy()) {
+ Context.setPrintingPolicy(PP);
+ }
+ ~LocalPrintingPolicyRAII() { Context.setPrintingPolicy(Policy); }
+ } X(Ctx, Policy);
+
+ const QualType NonRefTy = QT.getNonReferenceType();
+
+ if (const auto *TTy = llvm::dyn_cast<TagType>(NonRefTy))
+ return PrintDeclType(NonRefTy, TTy->getDecl());
+
+ if (const auto *TRy = dyn_cast<RecordType>(NonRefTy))
+ return PrintDeclType(NonRefTy, TRy->getDecl());
+
+ const QualType Canon = NonRefTy.getCanonicalType();
+
+ // FIXME: How a builtin type can be a function pointer type?
+ if (Canon->isBuiltinType() && !NonRefTy->isFunctionPointerType() &&
+ !NonRefTy->isMemberPointerType())
+ return Canon.getAsString(Ctx.getPrintingPolicy());
+
+ if (const auto *TDTy = dyn_cast<TypedefType>(NonRefTy)) {
+ // FIXME: TemplateSpecializationType & SubstTemplateTypeParmType checks
+ // are predominately to get STL containers to print nicer and might be
+ // better handled in GetFullyQualifiedName.
+ //
+ // std::vector<Type>::iterator is a TemplateSpecializationType
+ // std::vector<Type>::value_type is a SubstTemplateTypeParmType
+ //
+ QualType SSDesugar = TDTy->getLocallyUnqualifiedSingleStepDesugaredType();
+ if (llvm::isa<SubstTemplateTypeParmType>(SSDesugar))
+ return GetFullTypeName(Ctx, Canon);
+ else if (llvm::isa<TemplateSpecializationType>(SSDesugar))
+ return GetFullTypeName(Ctx, NonRefTy);
+ return PrintDeclType(NonRefTy, TDTy->getDecl());
+ }
+ return GetFullTypeName(Ctx, NonRefTy);
+}
+
+static std::string PrintEnum(const Value &V) {
+ std::string Str;
+ llvm::raw_string_ostream SS(Str);
+ ASTContext &Ctx = const_cast<ASTContext &>(V.getASTContext());
+
+ QualType DesugaredTy = V.getType().getDesugaredType(Ctx);
+ const EnumType *EnumTy = DesugaredTy.getNonReferenceType()->getAs<EnumType>();
+ assert(EnumTy && "Fail to cast to enum type");
+
+ EnumDecl *ED = EnumTy->getDecl();
+ uint64_t Data = V.getULongLong();
+ bool IsFirst = true;
+ llvm::APSInt AP = Ctx.MakeIntValue(Data, DesugaredTy);
+
+ for (auto I = ED->enumerator_begin(), E = ED->enumerator_end(); I != E; ++I) {
+ if (I->getInitVal() == AP) {
+ if (!IsFirst)
+ SS << " ? ";
+ SS << "(" + I->getQualifiedNameAsString() << ")";
+ IsFirst = false;
+ }
+ }
+ llvm::SmallString<64> APStr;
+ AP.toString(APStr, /*Radix=*/10);
+ SS << " : " << PrintQualType(Ctx, ED->getIntegerType()) << " " << APStr;
+ return Str;
+}
+
+static std::string PrintFunction(const Value &V, const void *Ptr) {
+ std::string Str;
+ llvm::raw_string_ostream SS(Str);
+ SS << "Function @" << Ptr;
+
+ const FunctionDecl *FD = nullptr;
+
+ auto Decls = V.getASTContext().getTranslationUnitDecl()->decls();
+ assert(std::distance(Decls.begin(), Decls.end()) == 1 &&
+ "TU should only contain one Decl");
+ auto *TLSD = llvm::cast<TopLevelStmtDecl>(*Decls.begin());
+
+ // Get __clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void
+ // *OpaqueType, void *Val);
+ if (auto *InterfaceCall = llvm::dyn_cast<CallExpr>(TLSD->getStmt())) {
+ const auto *Arg = InterfaceCall->getArg(/*Val*/ 3);
+ // Get rid of cast nodes.
+ while (const CastExpr *CastE = llvm::dyn_cast<CastExpr>(Arg))
+ Arg = CastE->getSubExpr();
+ if (const DeclRefExpr *DeclRefExp = llvm::dyn_cast<DeclRefExpr>(Arg))
+ FD = llvm::dyn_cast<FunctionDecl>(DeclRefExp->getDecl());
+
+ if (FD) {
+ SS << '\n';
+ const clang::FunctionDecl *FDef;
+ if (FD->hasBody(FDef))
+ FDef->print(SS);
+ }
+ }
+ return Str;
+}
+
+static std::string PrintAddress(const void *Ptr, char Prefix) {
+ std::string Str;
+ llvm::raw_string_ostream SS(Str);
+ if (!Ptr)
+ return Str;
+ SS << Prefix << Ptr;
+ return Str;
+}
+
+// FIXME: Encodings. Handle unprintable characters such as control characters.
+static std::string PrintOneChar(char Val) {
+ std::string Str;
+ llvm::raw_string_ostream SS(Str);
+
+ SS << "'" << Val << "'";
+ return Str;
+}
+
+// Char pointers
+// Assumption is this is a string.
+// N is limit to prevent endless loop if Ptr is not really a string.
+static std::string PrintString(const char *const *Ptr, size_t N = 10000) {
+ std::string Str;
+ llvm::raw_string_ostream SS(Str);
+
+ const char *Start = *Ptr;
+ if (!Start)
+ return "nullptr";
+
+ const char *End = Start + N;
+ // If we're gonna do this, better make sure the end is valid too
+ // FIXME: getpagesize() & GetSystemInfo().dwPageSize might be better
+ static constexpr auto PAGE_SIZE = 1024;
+ while (N > 1024) {
+ N -= PAGE_SIZE;
+ End = Start + N;
+ }
+
+ if (*Start == 0)
+ return "\"\"";
+
+ // Copy the bytes until we get a null-terminator
+ SS << "\"";
+ while (Start < End && *Start)
+ SS << *Start++;
+ SS << "\"";
+
+ return Str;
+}
+
+// Build the CallExpr to `PrintValueRuntime`.
+static void BuildWrapperBody(Interpreter &Interp, Sema &S, ASTContext &Ctx,
+ FunctionDecl *WrapperFD, QualType QT,
+ const void *ValPtr) {
+ Sema::SynthesizedFunctionScope SemaFScope(S, WrapperFD);
+ clang::DeclarationName RuntimeCallName;
+ if (Ctx.getLangOpts().CPlusPlus)
+ RuntimeCallName = S.PP.getIdentifierInfo("PrintValueRuntime");
+ else
+ RuntimeCallName =
+ S.PP.getIdentifierInfo("caas__runtime__PrintValueRuntime");
+
+ clang::LookupResult R(S, RuntimeCallName, SourceLocation(),
+ clang::Sema::LookupOrdinaryName);
+ S.LookupName(R, S.getCurScope());
+
+ Expr *OverldExpr = UnresolvedLookupExpr::Create(
+ Ctx, /*NamingClass=*/nullptr, NestedNameSpecifierLoc(),
+ clang::DeclarationNameInfo(RuntimeCallName, SourceLocation()),
+ /*RequiresADL=*/false, R.begin(), R.end(), /*KnownDependent=*/false,
+ /*KnownInstantiationDependent=*/false);
+
+ if (const auto *AT = llvm::dyn_cast<AutoType>(QT.getTypePtr())) {
+ if (AT->isDeduced())
+ QT = AT->getDeducedType().getDesugaredType(Ctx);
+ }
+
+ if (const auto *PT = llvm::dyn_cast<PointerType>(QT.getTypePtr())) {
+ // Normalize `X*` to `const void*`, invoke `printValue(const void**)`,
+ // unless it's a character string.
+ QualType QTPointeeUnqual = PT->getPointeeType().getUnqualifiedType();
+ if (!Ctx.hasSameType(QTPointeeUnqual, Ctx.CharTy) &&
+ !Ctx.hasSameType(QTPointeeUnqual, Ctx.WCharTy) &&
+ !Ctx.hasSameType(QTPointeeUnqual, Ctx.Char16Ty) &&
+ !Ctx.hasSameType(QTPointeeUnqual, Ctx.Char32Ty)) {
+ QT = Ctx.getPointerType(Ctx.VoidTy.withConst());
+ }
+ } else if (const auto *RTy = llvm::dyn_cast<ReferenceType>(QT.getTypePtr())) {
+ // X& will be printed as X* (the pointer will be added below).
+ QT = RTy->getPointeeType();
+ // Val will be a X**, but we cast this to X*, so dereference here:
+ ValPtr = *(const void *const *)ValPtr;
+ }
+
+ // `PrintValueRuntime()` takes the *address* of the value to be printed:
+ QualType QTPtr = Ctx.getPointerType(QT);
+ Expr *TypeArg = CStyleCastPtrExpr(S, QTPtr, (uintptr_t)ValPtr);
+ llvm::SmallVector<Expr *, 1> CallArgs = {TypeArg};
+
+ // Create the CallExpr.
+ ExprResult RuntimeCall =
+ S.ActOnCallExpr(S.getCurScope(), OverldExpr, SourceLocation(), CallArgs,
+ SourceLocation());
+ assert(!RuntimeCall.isInvalid() && "Cannot create call to PrintValueRuntime");
+
+ // Create the ReturnStmt.
+ StmtResult RetStmt =
+ S.ActOnReturnStmt(SourceLocation(), RuntimeCall.get(), S.getCurScope());
+ assert(!RetStmt.isInvalid() && "Cannot create ReturnStmt");
+
+ // Create the CompoundStmt.
+ StmtResult Body =
+ CompoundStmt::Create(Ctx, {RetStmt.get()}, FPOptionsOverride(),
+ SourceLocation(), SourceLocation());
+ assert(!Body.isInvalid() && "Cannot create function body");
+
+ WrapperFD->setBody(Body.get());
+ // Add attribute `__attribute__((used))`.
+ WrapperFD->addAttr(UsedAttr::CreateImplicit(Ctx));
+}
+
+static constexpr const char *const WrapperName = "__InterpreterCallPrint";
+
+static llvm::Expected<llvm::orc::ExecutorAddr> CompileDecl(Interpreter &Interp,
+ Decl *D) {
+ assert(D && "The Decl being compiled can't be null");
+
+ ASTConsumer &Consumer = Interp.getCompilerInstance()->getASTConsumer();
+ Consumer.HandleTopLevelDecl(DeclGroupRef(D));
+ Interp.getCompilerInstance()->getSema().PerformPendingInstantiations();
+ ASTContext &C = Interp.getASTContext();
+ TranslationUnitDecl *TUPart = C.getTranslationUnitDecl();
+ assert(!TUPart->containsDecl(D) && "Decl already added!");
+ TUPart->addDecl(D);
+ Consumer.HandleTranslationUnit(C);
+
+ if (std::unique_ptr<llvm::Module> M = Interp.GenModule()) {
+ PartialTranslationUnit PTU = {TUPart, std::move(M)};
+ if (llvm::Error Err = Interp.Execute(PTU))
+ return Err;
+ ASTNameGenerator ASTNameGen(Interp.getASTContext());
+ llvm::Expected<llvm::orc::ExecutorAddr> AddrOrErr =
+ Interp.getSymbolAddressFromLinkerName(ASTNameGen.getName(D));
+
+ return AddrOrErr;
+ }
+ return llvm::orc::ExecutorAddr{};
+}
+
+static std::string CreateUniqName(std::string Base) {
+ static size_t I = 0;
+ Base += std::to_string(I);
+ I += 1;
+ return Base;
+}
+
+REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const void *Ptr) {
+ return PrintAddress(Ptr, '@');
+}
+
+REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const void **Ptr) {
+ return PrintAddress(*Ptr, '@');
+}
+
+REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const bool *Val) {
+ if (*Val)
+ return "true";
+ return "false";
+}
+
+REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const char *Val) {
+ return PrintOneChar(*Val);
+}
+
+REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const signed char *Val) {
+ return PrintOneChar(*Val);
+}
+
+REPL_EXTERNAL_VISIBILITY std::string
+PrintValueRuntime(const unsigned char *Val) {
+ return PrintOneChar(*Val);
+}
+
+REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const short *Val) {
+ std::string Str;
+ llvm::raw_string_ostream SS(Str);
+ SS << *Val;
+ return Str;
+}
+
+REPL_EXTERNAL_VISIBILITY
+std::string PrintValueRuntime(const unsigned short *Val) {
+ std::string Str;
+ llvm::raw_string_ostream SS(Str);
+ SS << *Val;
+ return Str;
+}
+
+REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const int *Val) {
+ std::string Str;
+ llvm::raw_string_ostream SS(Str);
+ SS << *Val;
+ return Str;
+}
+
+REPL_EXTERNAL_VISIBILITY
+std::string PrintValueRuntime(const unsigned int *Val) {
+ std::string Str;
+ llvm::raw_string_ostream SS(Str);
+ SS << *Val;
+ return Str;
+}
+
+REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const long *Val) {
+ std::string Str;
+ llvm::raw_string_ostream SS(Str);
+ SS << *Val;
+ return Str;
+}
+
+REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const long long *Val) {
+ std::string Str;
+ llvm::raw_string_ostream SS(Str);
+ SS << *Val;
+ return Str;
+}
+
+REPL_EXTERNAL_VISIBILITY
+std::string PrintValueRuntime(const unsigned long *Val) {
+ std::string Str;
+ llvm::raw_string_ostream SS(Str);
+ SS << *Val;
+ return Str;
+}
+
+REPL_EXTERNAL_VISIBILITY
+std::string PrintValueRuntime(const unsigned long long *Val) {
+ std::string Str;
+ llvm::raw_string_ostream SS(Str);
+ SS << *Val;
+ return Str;
+}
+
+REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const float *Val) {
+ std::string Str;
+ llvm::raw_string_ostream SS(Str);
+ SS << llvm::format("%#.6g", *Val) << 'f';
+ return Str;
+}
+
+REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const double *Val) {
+ std::string Str;
+ llvm::raw_string_ostream SS(Str);
+ SS << llvm::format("%#.12g", *Val);
+ return Str;
+}
+
+REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const long double *Val) {
+ std::string Str;
+ llvm::raw_string_ostream SS(Str);
+ SS << llvm::format("%#.8Lg", *Val) << 'L';
+ return Str;
+}
+
+REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const char *const *Val) {
+ return PrintString(Val);
+}
+
+REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const char **Val) {
+ return PrintString(Val);
+}
+
+namespace clang {
+std::string Interpreter::ValueDataToString(const Value &V) {
+ QualType QT = V.getType();
+ QualType DesugaredTy = QT.getDesugaredType(V.getASTContext());
+ QualType NonRefTy = DesugaredTy.getNonReferenceType();
+
+ if (NonRefTy->isEnumeralType())
+ return PrintEnum(V);
+
+ if (NonRefTy->isFunctionType())
+ return PrintFunction(V, &V);
+
+ if ((NonRefTy->isPointerType() || NonRefTy->isMemberPointerType()) &&
+ NonRefTy->getPointeeType()->isFunctionProtoType())
+ return PrintFunction(V, V.getPtr());
+
+ if (NonRefTy->isNullPtrType())
+ return "nullptr\n";
+
+ // If it is a builtin type dispatch to the builtin overloads.
+ if (auto *BT = DesugaredTy.getCanonicalType()->getAs<BuiltinType>()) {
+ switch (BT->getKind()) {
+ default:
+ return "{ unknown builtin type: }" + std::to_string(BT->getKind());
+#define X(type, name) \
+ case clang::BuiltinType::name: { \
+ type val = V.get##name(); \
+ return PrintValueRuntime(&val); \
+ }
+ REPL_BUILTIN_TYPES
+#undef X
+ }
+ }
+ if (auto *CXXRD = NonRefTy->getAsCXXRecordDecl())
+ if (CXXRD->isLambda())
+ return PrintAddress(V.getPtr(), '@');
+
+ // All fails then generate a runtime call, this is slow.
+ Sema &S = getCompilerInstance()->getSema();
+ ASTContext &Ctx = S.getASTContext();
+
+ QualType RetTy;
+ if (Ctx.getLangOpts().CPlusPlus && !StdString) {
+
+ // Only include the header on demand because it's very heavy.
+ if (llvm::Error E = ParseAndExecute(
+ "#include <__clang_interpreter_runtime_printvalue.h>")) {
+ llvm::logAllUnhandledErrors(std::move(E), llvm::errs(), "Parsing failed");
+ return "{Internal error}";
+ }
+
+ // Find and cache std::string.
+ NamespaceDecl *Std = LookupNamespace(S, "std");
+ assert(Std && "Cannot find namespace std");
+ Decl *StdStringDecl = LookupNamed(S, "string", Std);
+ assert(StdStringDecl && "Cannot find std::string");
+ const auto *StdStringTyDecl = llvm::dyn_cast<TypeDecl>(StdStringDecl);
+ assert(StdStringTyDecl && "Cannot find type of std::string");
+ RetTy = QualType(StdStringTyDecl->getTypeForDecl(), /*Quals=*/0);
+ } else {
+ RetTy = Ctx.getPointerType(Ctx.CharTy.withConst());
+ }
+
+ // Create the wrapper function.
+ DeclarationName DeclName = &Ctx.Idents.get(CreateUniqName(WrapperName));
+ QualType FnTy =
+ Ctx.getFunctionType(RetTy, {}, FunctionProtoType::ExtProtoInfo());
+ auto *WrapperFD = FunctionDecl::Create(
+ Ctx, Ctx.getTranslationUnitDecl(), SourceLocation(), SourceLocation(),
+ DeclName, FnTy, Ctx.getTrivialTypeSourceInfo(FnTy), SC_None);
+
+ void *ValPtr = V.getPtr();
+
+ // FIXME: We still need to understand why we have to get the pointer to the
+ // underlying Value storage for this to work reliabily...
+ if (!V.isManuallyAlloc())
+ ValPtr = V.getPtrAddress();
+
+ BuildWrapperBody(*this, S, Ctx, WrapperFD, V.getType(), ValPtr);
+
+ auto AddrOrErr = CompileDecl(*this, WrapperFD);
+ if (!AddrOrErr)
+ llvm::logAllUnhandledErrors(AddrOrErr.takeError(), llvm::errs(),
+ "Fail to get symbol address");
+ if (auto *Main = AddrOrErr->toPtr<std::string (*)()>())
+ return (*Main)();
+ return "Unable to print the value!";
+}
+
+std::string Interpreter::ValueTypeToString(const Value &V) const {
+ ASTContext &Ctx = const_cast<ASTContext &>(V.getASTContext());
+ QualType QT = V.getType();
+
+ std::string QTStr = PrintQualType(Ctx, QT);
+
+ if (QT->isReferenceType())
+ QTStr += " &";
+
+ return QTStr;
+}
+
+llvm::Expected<llvm::orc::ExecutorAddr>
+Interpreter::CompileDtorCall(CXXRecordDecl *CXXRD) {
+ assert(CXXRD && "Cannot compile a destructor for a nullptr");
+ if (auto Dtor = Dtors.find(CXXRD); Dtor != Dtors.end())
+ return Dtor->getSecond();
+
+ if (CXXRD->hasIrrelevantDestructor())
+ return llvm::orc::ExecutorAddr{};
+
+ CXXDestructorDecl *DtorRD =
+ getCompilerInstance()->getSema().LookupDestructor(CXXRD);
+
+ llvm::StringRef Name =
+ getCodeGen()->GetMangledName(GlobalDecl(DtorRD, Dtor_Base));
+ auto AddrOrErr = getSymbolAddress(Name);
+ if (!AddrOrErr)
+ return AddrOrErr.takeError();
+
+ Dtors[CXXRD] = *AddrOrErr;
+ return AddrOrErr;
+}
+
+enum InterfaceKind { NoAlloc, WithAlloc, CopyArray, NewTag };
+
+class InterfaceKindVisitor
+ : public TypeVisitor<InterfaceKindVisitor, InterfaceKind> {
+
+ Sema &S;
+ Expr *E;
+ llvm::SmallVectorImpl<Expr *> &Args;
+
+public:
+ InterfaceKindVisitor(Sema &S, Expr *E, llvm::SmallVectorImpl<Expr *> &Args)
+ : S(S), E(E), Args(Args) {}
+
+ InterfaceKind computeInterfaceKind(QualType Ty) {
+ return Visit(Ty.getTypePtr());
+ }
+
+ InterfaceKind VisitRecordType(const RecordType *Ty) {
+ return InterfaceKind::WithAlloc;
+ }
+
+ InterfaceKind VisitMemberPointerType(const MemberPointerType *Ty) {
+ return InterfaceKind::WithAlloc;
+ }
+
+ InterfaceKind VisitConstantArrayType(const ConstantArrayType *Ty) {
+ return InterfaceKind::CopyArray;
+ }
+
+ InterfaceKind VisitFunctionProtoType(const FunctionProtoType *Ty) {
+ HandlePtrType(Ty);
+ return InterfaceKind::NoAlloc;
+ }
+
+ InterfaceKind VisitPointerType(const PointerType *Ty) {
+ HandlePtrType(Ty);
+ return InterfaceKind::NoAlloc;
+ }
+
+ InterfaceKind VisitReferenceType(const ReferenceType *Ty) {
+ ExprResult AddrOfE = S.CreateBuiltinUnaryOp(SourceLocation(), UO_AddrOf, E);
+ assert(!AddrOfE.isInvalid() && "Can not create unary expression");
+ Args.push_back(AddrOfE.get());
+ return InterfaceKind::NoAlloc;
+ }
+
+ InterfaceKind VisitBuiltinType(const BuiltinType *Ty) {
+ if (Ty->isNullPtrType())
+ Args.push_back(E);
+ else if (Ty->isFloatingType())
+ Args.push_back(E);
+ else if (Ty->isIntegralOrEnumerationType())
+ HandleIntegralOrEnumType(Ty);
+ else if (Ty->isVoidType()) {
+ // Do we need to still run `E`?
+ }
+
+ return InterfaceKind::NoAlloc;
+ }
+
+ InterfaceKind VisitEnumType(const EnumType *Ty) {
+ HandleIntegralOrEnumType(Ty);
+ return InterfaceKind::NoAlloc;
+ }
+
+private:
+ // Force cast these types to the uint that fits the register size. That way we
+ // reduce the number of overloads of `__clang_Interpreter_SetValueNoAlloc`.
+ void HandleIntegralOrEnumType(const Type *Ty) {
+ ASTContext &Ctx = S.getASTContext();
+ uint64_t PtrBits = Ctx.getTypeSize(Ctx.VoidPtrTy);
+ QualType UIntTy = Ctx.getBitIntType(/*Unsigned=*/true, PtrBits);
+ TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(UIntTy);
+ ExprResult CastedExpr =
+ S.BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), E);
+ assert(!CastedExpr.isInvalid() && "Cannot create cstyle cast expr");
+ Args.push_back(CastedExpr.get());
+ }
+
+ void HandlePtrType(const Type *Ty) {
+ ASTContext &Ctx = S.getASTContext();
+ TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ctx.VoidPtrTy);
+ ExprResult CastedExpr =
+ S.BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), E);
+ assert(!CastedExpr.isInvalid() && "Can not create cstyle cast expression");
+ Args.push_back(CastedExpr.get());
+ }
+};
+
+// This synthesizes a call expression to a speciall
+// function that is responsible for generating the Value.
+// In general, we transform:
+// clang-repl> x
+// To:
+// // 1. If x is a built-in type like int, float.
+// __clang_Interpreter_SetValueNoAlloc(ThisInterp, OpaqueValue, xQualType, x);
+// // 2. If x is a struct, and a lvalue.
+// __clang_Interpreter_SetValueNoAlloc(ThisInterp, OpaqueValue, xQualType,
+// &x);
+// // 3. If x is a struct, but a rvalue.
+// new (__clang_Interpreter_SetValueWithAlloc(ThisInterp, OpaqueValue,
+// xQualType)) (x);
+llvm::Expected<Expr *> Interpreter::AttachValuePrinting(Expr *E) {
+ Sema &S = getCompilerInstance()->getSema();
+ ASTContext &Ctx = S.getASTContext();
+
+ // Find the value printing builtins.
+ if (!ValuePrintingInfo[0]) {
+ assert(llvm::all_of(ValuePrintingInfo, [](Expr *E) { return !E; }));
+
+ auto LookupInterface = [&](Expr *&Interface,
+ llvm::StringRef Name) -> llvm::Error {
+ LookupResult R(S, &Ctx.Idents.get(Name), SourceLocation(),
+ Sema::LookupOrdinaryName,
+ RedeclarationKind::ForVisibleRedeclaration);
+ S.LookupQualifiedName(R, Ctx.getTranslationUnitDecl());
+ if (R.empty())
+ return llvm::make_error<llvm::StringError>(
+ Name + " not found!", llvm::inconvertibleErrorCode());
+
+ CXXScopeSpec CSS;
+ Interface = S.BuildDeclarationNameExpr(CSS, R, /*ADL=*/false).get();
+ return llvm::Error::success();
+ };
+ static constexpr llvm::StringRef Builtin[] = {
+ "__clang_Interpreter_SetValueNoAlloc",
+ "__clang_Interpreter_SetValueWithAlloc",
+ "__clang_Interpreter_SetValueCopyArr", "__ci_newtag"};
+ if (llvm::Error Err =
+ LookupInterface(ValuePrintingInfo[NoAlloc], Builtin[NoAlloc]))
+ return std::move(Err);
+
+ if (Ctx.getLangOpts().CPlusPlus) {
+ if (llvm::Error Err =
+ LookupInterface(ValuePrintingInfo[WithAlloc], Builtin[WithAlloc]))
+ return std::move(Err);
+ if (llvm::Error Err =
+ LookupInterface(ValuePrintingInfo[CopyArray], Builtin[CopyArray]))
+ return std::move(Err);
+ if (llvm::Error Err =
+ LookupInterface(ValuePrintingInfo[NewTag], Builtin[NewTag]))
+ return std::move(Err);
+ }
+ }
+
+ llvm::SmallVector<Expr *, 4> AdjustedArgs;
+ // Create parameter `ThisInterp`.
+ AdjustedArgs.push_back(CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)this));
+
+ // Create parameter `OutVal`.
+ AdjustedArgs.push_back(
+ CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)&LastValue));
+
+ // Build `__clang_Interpreter_SetValue*` call.
+
+ // Get rid of ExprWithCleanups.
+ if (auto *EWC = llvm::dyn_cast_if_present<ExprWithCleanups>(E))
+ E = EWC->getSubExpr();
+
+ QualType Ty = E->getType();
+ QualType DesugaredTy = Ty.getDesugaredType(Ctx);
+
+ // For lvalue struct, we treat it as a reference.
+ if (DesugaredTy->isRecordType() && E->isLValue()) {
+ DesugaredTy = Ctx.getLValueReferenceType(DesugaredTy);
+ Ty = Ctx.getLValueReferenceType(Ty);
+ }
+
+ Expr *TypeArg =
+ CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)Ty.getAsOpaquePtr());
+ // The QualType parameter `OpaqueType`, represented as `void*`.
+ AdjustedArgs.push_back(TypeArg);
+
+ // We push the last parameter based on the type of the Expr. Note we need
+ // special care for rvalue struct.
+ InterfaceKindVisitor V(S, E, AdjustedArgs);
+ Scope *Scope = nullptr;
+ ExprResult SetValueE;
+ InterfaceKind Kind = V.computeInterfaceKind(DesugaredTy);
+ switch (Kind) {
+ case InterfaceKind::WithAlloc:
+ LLVM_FALLTHROUGH;
+ case InterfaceKind::CopyArray: {
+ // __clang_Interpreter_SetValueWithAlloc.
+ ExprResult AllocCall =
+ S.ActOnCallExpr(Scope, ValuePrintingInfo[InterfaceKind::WithAlloc],
+ E->getBeginLoc(), AdjustedArgs, E->getEndLoc());
+ assert(!AllocCall.isInvalid() && "Can't create runtime interface call!");
+
+ TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ty, SourceLocation());
+
+ // Force CodeGen to emit destructor.
+ if (auto *RD = Ty->getAsCXXRecordDecl()) {
+ auto *Dtor = S.LookupDestructor(RD);
+ Dtor->addAttr(UsedAttr::CreateImplicit(Ctx));
+ getCompilerInstance()->getASTConsumer().HandleTopLevelDecl(
+ DeclGroupRef(Dtor));
+ }
+
+ // __clang_Interpreter_SetValueCopyArr.
+ if (Kind == InterfaceKind::CopyArray) {
+ const auto *ConstantArrTy =
+ cast<ConstantArrayType>(DesugaredTy.getTypePtr());
+ size_t ArrSize = Ctx.getConstantArrayElementCount(ConstantArrTy);
+ Expr *ArrSizeExpr = IntegerLiteralExpr(Ctx, ArrSize);
+ Expr *Args[] = {E, AllocCall.get(), ArrSizeExpr};
+ SetValueE =
+ S.ActOnCallExpr(Scope, ValuePrintingInfo[InterfaceKind::CopyArray],
+ SourceLocation(), Args, SourceLocation());
+ }
+ Expr *Args[] = {AllocCall.get(), ValuePrintingInfo[InterfaceKind::NewTag]};
+ ExprResult CXXNewCall = S.BuildCXXNew(
+ E->getSourceRange(),
+ /*UseGlobal=*/true, /*PlacementLParen=*/SourceLocation(), Args,
+ /*PlacementRParen=*/SourceLocation(),
+ /*TypeIdParens=*/SourceRange(), TSI->getType(), TSI, std::nullopt,
+ E->getSourceRange(), E);
+
+ assert(!CXXNewCall.isInvalid() &&
+ "Can't create runtime placement new call!");
+
+ SetValueE = S.ActOnFinishFullExpr(CXXNewCall.get(),
+ /*DiscardedValue=*/false);
+ break;
+ }
+ // __clang_Interpreter_SetValueNoAlloc.
+ case InterfaceKind::NoAlloc: {
+ SetValueE =
+ S.ActOnCallExpr(Scope, ValuePrintingInfo[InterfaceKind::NoAlloc],
+ E->getBeginLoc(), AdjustedArgs, E->getEndLoc());
+ break;
+ }
+ default:
+ llvm_unreachable("Unhandled InterfaceKind");
+ }
+
+ // It could fail, like printing an array type in C. (not supported)
+ if (SetValueE.isInvalid())
+ return E;
+
+ return SetValueE.get();
+}
+
+} // namespace clang
+
+// Temporary rvalue struct that need special care.
+REPL_EXTERNAL_VISIBILITY void *
+__clang_Interpreter_SetValueWithAlloc(void *This, void *OutVal,
+ void *OpaqueType) {
+ Value &VRef = *(Value *)OutVal;
+ VRef = Value(static_cast<Interpreter *>(This), OpaqueType);
+ return VRef.getPtr();
+}
+
+extern "C" void REPL_EXTERNAL_VISIBILITY __clang_Interpreter_SetValueNoAlloc(
+ void *This, void *OutVal, void *OpaqueType, ...) {
+ Value &VRef = *(Value *)OutVal;
+ Interpreter *I = static_cast<Interpreter *>(This);
+ VRef = Value(I, OpaqueType);
+ if (VRef.isVoid())
+ return;
+
+ va_list args;
+ va_start(args, /*last named param*/ OpaqueType);
+
+ QualType QT = VRef.getType();
+ if (VRef.getKind() == Value::K_PtrOrObj) {
+ VRef.setPtr(va_arg(args, void *));
+ } else {
+ if (const auto *ET = QT->getAs<EnumType>())
+ QT = ET->getDecl()->getIntegerType();
+ switch (QT->castAs<BuiltinType>()->getKind()) {
+ default:
+ llvm_unreachable("unknown type kind!");
+ break;
+ // Types shorter than int are resolved as int, else va_arg has UB.
+ case BuiltinType::Bool:
+ VRef.setBool(va_arg(args, int));
+ break;
+ case BuiltinType::Char_S:
+ VRef.setChar_S(va_arg(args, int));
+ break;
+ case BuiltinType::SChar:
+ VRef.setSChar(va_arg(args, int));
+ break;
+ case BuiltinType::Char_U:
+ VRef.setChar_U(va_arg(args, unsigned));
+ break;
+ case BuiltinType::UChar:
+ VRef.setUChar(va_arg(args, unsigned));
+ break;
+ case BuiltinType::Short:
+ VRef.setShort(va_arg(args, int));
+ break;
+ case BuiltinType::UShort:
+ VRef.setUShort(va_arg(args, unsigned));
+ break;
+ case BuiltinType::Int:
+ VRef.setInt(va_arg(args, int));
+ break;
+ case BuiltinType::UInt:
+ VRef.setUInt(va_arg(args, unsigned));
+ break;
+ case BuiltinType::Long:
+ VRef.setLong(va_arg(args, long));
+ break;
+ case BuiltinType::ULong:
+ VRef.setULong(va_arg(args, unsigned long));
+ break;
+ case BuiltinType::LongLong:
+ VRef.setLongLong(va_arg(args, long long));
+ break;
+ case BuiltinType::ULongLong:
+ VRef.setULongLong(va_arg(args, unsigned long long));
+ break;
+ // Types shorter than double are resolved as double, else va_arg has UB.
+ case BuiltinType::Float:
+ VRef.setFloat(va_arg(args, double));
+ break;
+ case BuiltinType::Double:
+ VRef.setDouble(va_arg(args, double));
+ break;
+ case BuiltinType::LongDouble:
+ VRef.setLongDouble(va_arg(args, long double));
+ break;
+ // See REPL_BUILTIN_TYPES.
+ }
+ }
+ va_end(args);
+}
+
+// A trampoline to work around the fact that operator placement new cannot
+// really be forward declared due to libc++ and libstdc++ declaration mismatch.
+// FIXME: __clang_Interpreter_NewTag is ODR violation because we get the same
+// definition in the interpreter runtime. We should move it in a runtime header
+// which gets included by the interpreter and here.
+struct __clang_Interpreter_NewTag {};
+REPL_EXTERNAL_VISIBILITY void *
+operator new(size_t __sz, void *__p, __clang_Interpreter_NewTag) noexcept {
+ // Just forward to the standard operator placement new.
+ return operator new(__sz, __p);
+}
diff --git a/clang/lib/Interpreter/Value.cpp b/clang/lib/Interpreter/Value.cpp
index eb2ce9c9fd3302..0de66b4f0e699b 100644
--- a/clang/lib/Interpreter/Value.cpp
+++ b/clang/lib/Interpreter/Value.cpp
@@ -12,6 +12,7 @@
//===----------------------------------------------------------------------===//
#include "clang/Interpreter/Value.h"
+#include "InterpreterUtils.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Type.h"
#include "clang/Interpreter/Interpreter.h"
@@ -22,6 +23,8 @@
#include <cstdint>
#include <utility>
+using namespace clang;
+
namespace {
// This is internal buffer maintained by Value, used to hold temporaries.
@@ -231,6 +234,11 @@ void *Value::getPtr() const {
return Data.m_Ptr;
}
+void **Value::getPtrAddress() const {
+ assert(ValueKind == K_PtrOrObj);
+ return &const_cast<Value *>(this)->Data.m_Ptr;
+}
+
QualType Value::getType() const {
return QualType::getFromOpaquePtr(OpaqueType);
}
@@ -253,17 +261,35 @@ const ASTContext &Value::getASTContext() const {
return getInterpreter().getASTContext();
}
-void Value::dump() const { print(llvm::outs()); }
+void Value::dump() { print(llvm::outs()); }
void Value::printType(llvm::raw_ostream &Out) const {
- Out << "Not implement yet.\n";
+ Out << Interp->ValueTypeToString(*this);
}
-void Value::printData(llvm::raw_ostream &Out) const {
- Out << "Not implement yet.\n";
+
+void Value::printData(llvm::raw_ostream &Out) {
+ Out << Interp->ValueDataToString(*this);
}
-void Value::print(llvm::raw_ostream &Out) const {
+// FIXME: We do not support the multiple inheritance case where one of the base
+// classes has a pretty-printer and the other does not.
+void Value::print(llvm::raw_ostream &Out) {
assert(OpaqueType != nullptr && "Can't print default Value");
- Out << "Not implement yet.\n";
+
+ // Don't even try to print a void or an invalid type, it doesn't make sense.
+ if (getType()->isVoidType() || !isValid())
+ return;
+
+ // We need to get all the results together then print it, since `printType` is
+ // much faster than `printData`.
+ std::string Str;
+ llvm::raw_string_ostream SS(Str);
+
+ SS << "(";
+ printType(SS);
+ SS << ") ";
+ printData(SS);
+ SS << "\n";
+ Out << Str;
}
} // namespace clang
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index bdb3fc051d0b35..56b1141f02a463 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -583,7 +583,8 @@ StmtResult Parser::ParseExprStatement(ParsedStmtContext StmtCtx) {
}
Token *CurTok = nullptr;
- // Note we shouldn't eat the token since the callback needs it.
+ // If the semicolon is missing at the end of REPL input, we want to print
+ // the result. Note we shouldn't eat the token since the callback needs it.
if (Tok.is(tok::annot_repl_input_end))
CurTok = &Tok;
else
diff --git a/clang/test/Interpreter/pretty-print.c b/clang/test/Interpreter/pretty-print.c
index d21749a649e1c0..927cd836ab693b 100644
--- a/clang/test/Interpreter/pretty-print.c
+++ b/clang/test/Interpreter/pretty-print.c
@@ -3,9 +3,73 @@
// RUN: cat %s | clang-repl -Xcc -xc | FileCheck %s
// RUN: cat %s | clang-repl -Xcc -std=c++11 | FileCheck %s
-// Fails with `Symbols not found: [ __clang_Interpreter_SetValueNoAlloc ]`.
// UNSUPPORTED: hwasan
const char* c_str = "Hello, world!"; c_str
-// CHECK: Not implement yet.
+char c = 'a'; c
+// CHECK: (char) 'a'
+
+c_str = "Goodbye, world!"; c_str
+// CHECK-NEXT: (const char *) "Goodbye, world!"
+
+const char* c_null_str = 0; c_null_str
+// CHECK-NEXT: (const char *) nullptr
+
+"Hello, world"
+// CHECK-NEXT: (const char[13]) "Hello, world"
+
+int x = 42; x
+// CHECK-NEXT: (int) 42
+
+&x
+// CHECK-NEXT: (int *) @0x{{[0-9a-f]+}}
+
+x - 2
+// CHECK-NEXT: (int) 40
+
+float f = 4.2f; f
+// CHECK-NEXT: (float) 4.20000f
+
+double d = 4.21; d
+// CHECK-NEXT: (double) 4.21000000000
+
+struct S1{} s1; s1
+// CHECK-NEXT: (S1 &) @0x{{[0-9a-f]+}}
+
+S1{}
+// CHECK-NEXT: (S1) @0x{{[0-9a-f]+}}
+
+struct S2 {int d;} E = {22}; E
+// CHECK-NEXT: (struct S2 &) @0x{{[0-9a-f]+}}
+E.d
+// CHECK-NEXT: (int) 22
+
+// Arrays.
+
+int arr[3] = {1,2,3}; arr
+// CHECK-NEXT: (int[3]) { 1, 2, 3 }
+
+int foo() { return 42; } foo()
+// CHECK-NEXT: (int) 42
+
+void bar() {} bar()
+
+struct ConstLiteral{};
+const char * caas__runtime__PrintValueRuntime(const struct ConstLiteral *) { \
+ return "ConstLiteral"; \
+}
+struct ConstLiteral CL; CL
+// CHECK-NEXT: ConstLiteral
+
+struct Point{int x; int y};
+const char * caas__runtime__PrintValueRuntime(const struct Point *p) { \
+ char[11 + 11 + 4 + 1] result; \
+ sprintf(result, "(%d, %d)", p->x, p->y); \
+ return strdup(result); \
+}
+
+Point P {1,2}; P
+// CHECK-NEXT: (1, 2)
+
+%quit
diff --git a/clang/test/Interpreter/pretty-print.cpp b/clang/test/Interpreter/pretty-print.cpp
new file mode 100644
index 00000000000000..b7a6c07c481dd3
--- /dev/null
+++ b/clang/test/Interpreter/pretty-print.cpp
@@ -0,0 +1,170 @@
+// RUN: clang-repl "int i = 10;" 'extern "C" int printf(const char*,...);' \
+// RUN: 'auto r1 = printf("i = %d\n", i);' | FileCheck --check-prefix=CHECK-DRIVER %s
+// UNSUPPORTED: system-aix
+// CHECK-DRIVER: i = 10
+// RUN: cat %s | clang-repl -Xcc -std=c++11 -Xcc -fno-delayed-template-parsing | FileCheck %s
+extern "C" int printf(const char*,...);
+
+struct NonPOD { \
+ static int sI; \
+ int I; \
+ NonPOD(): I(sI++) {} \
+};
+namespace caas { namespace runtime { \
+ const char* PrintValueRuntime(const NonPOD* type) { \
+ switch (type->I) { \
+ default: return "out-of-bounds"; \
+ case 0: return "0"; case 1: return "1"; case 2: return "2"; \
+ case 3: return "3"; case 4: return "4"; case 5: return "5"; \
+ } \
+}}}
+
+int NonPOD::sI = 0;
+
+NonPOD non_pod_arr[2][3];
+// Check array order after the value printing transformation. Also make sure we
+// can handle the forward declaration of operator new with placement.
+non_pod_arr
+// CHECK: (NonPOD[2][3]) { { 0, 1, 2 }, { 3, 4, 5 } }
+
+struct S3 { int* p; S3() { p = new int(42); } ~S3() { delete p; } };
+S3{}
+// CHECK-NEXT: (S3) @0x{{[0-9a-f]+}}
+S3 s3;
+s3
+// CHECK-NEXT: (S3 &) @0x{{[0-9a-f]+}}
+
+struct S4 { ~S4() { printf("~S4()\n"); }};
+S4{}
+// CHECK-NEXT: (S4) @0x{{[0-9a-f]+}}
+
+enum Enum{ e1 = -12, e2, e3=33, e4, e5 = 33};
+e2
+// CHECK-NEXT: (Enum) (e2) : int -11
+::e1
+// CHECK-NEXT: (Enum) (e1) : int -12
+
+enum class Color { Black = 0, Red, Green };
+Color::Black
+// CHECK-NEXT: (Color) (Color::Black) : int 0
+
+
+// Lambdas.
+
+auto Lambda1 = []{};
+Lambda1
+// CHECK-NEXT: ((lambda) &) @0x{{[0-9a-f]+}}
+[]{}
+// CHECK-NEXT: ((lambda at input_line_{{[0-9]+}}:1:1)) @0x{{[0-9a-f]+}}
+
+template<int n> struct F{ enum {RET=F<n-1>::RET*n} ; };
+template<> struct F<0> { enum {RET = 1}; };
+F<7>::RET
+// CHECK-NEXT: (F<7>::(unnamed enum at input_line_{{[0-9]+}}:1:27)) (F<7>::RET) : unsigned int 5040
+
+struct S5 { int foo() { return 42; }};
+&S5::foo
+// CHECK-NEXT: (int (S5::*)()) Function @0x{{[0-9a-f]+}}
+
+#include <memory>
+
+auto p1 = std::make_shared<int>(42);
+p1
+// CHECK-NEXT: (std::shared_ptr<int> &) std::shared_ptr -> @0x{{[0-9a-f]+}}
+
+std::unique_ptr<int> p2(new int(42));
+p2
+// CHECK-NEXT: (std::unique_ptr<int> &) std::unique_ptr -> @0x{{[0-9a-f]+}}
+
+#include <array>
+std::array<int, 3> a{1, 2, 3};
+a
+// CHECK-NEXT: (std::array<int, 3> &) { 1, 2, 3 }
+
+#include <vector>
+std::vector<int> v1 = {7, 5, 16, 8};
+v1
+// CHECK-NEXT: (std::vector<int> &) { 7, 5, 16, 8 }
+
+std::vector<bool> v = {true, false, true};
+v
+// CHECK-NEXT: (std::vector<bool> &) { true, false, true }
+
+#include <deque>
+std::deque<int> dq = {7, 5, 16, 8};
+dq
+// CHECK-NEXT: (std::deque<int> &) { 7, 5, 16, 8 }
+
+#include <forward_list>
+std::forward_list<int> fl {3,4,5,6};
+fl
+// CHECK-NEXT: (std::forward_list<int> &) { 3, 4, 5, 6 }
+
+#include <set>
+std::set<int> z1 = {2,4,6,8};
+z1
+// CHECK-NEXT: (std::set<int> &) { 2, 4, 6, 8 }
+
+#include <unordered_set>
+std::unordered_set<int> z2 = {8,2,4,6};
+z2
+// CHECK-NEXT: (std::unordered_set<int> &) { [[Num:[0-9]+]], [[Num:[0-9]+]], [[Num:[0-9]+]], [[Num:[0-9]+]] }
+
+std::multiset<int> e {3,2,1,2,4,7,3};
+e
+// CHECK-NEXT: (std::multiset<int> &) { 1, 2, 2, 3, 3, 4, 7 }
+
+#include <string>
+std::string std_str = "Hello, world!";
+std_str
+// CHECK-NEXT: (std::string &) "Hello, world!"
+
+#include <utility>
+std::pair<int,char> pr(42,'a');
+pr
+// CHECK-NEXT: (std::pair<int, char> &) { 42, 'a' }
+
+#include <tuple>
+std::tuple<int,double,char> tu(42,3.14,'a');
+tu
+// CHECK-NEXT: (std::tuple<int, double, char> &) { 42, 3.14000000000, 'a' }
+
+#include <map>
+std::map<const char*, int> m1{{"CPU", 10}, {"GPU", 15}, {"RAM", 20}};
+m1
+// CHECK-NEXT: (std::map<const char *, int> &) { "CPU" => 10, "GPU" => 15, "RAM" => 20 }
+
+#include <unordered_map>
+std::unordered_map<int, int> m2 = { {1,2}, {3,4}};
+m2
+// CHECK-NEXT: (std::unordered_map<int, int> &) { [[Num:[0-9]+]] => [[Num:[0-9]+]], [[Num:[0-9]+]] => [[Num:[0-9]+]] }
+
+struct MyDate { \
+ unsigned year; \
+ char month; \
+ char day; \
+};
+
+#include <string>
+
+namespace caas { namespace runtime { \
+ std::string PrintValueRuntime(const MyDate* d) { \
+ std::string result; \
+ result = std::to_string(d->year) + '-'; \
+ switch(d->month) { default: result += "invalid month"; break; \
+ case 1: result += "Jan"; break; case 2: result += "Feb"; break; \
+ case 3: result += "Mar"; break; case 4: result += "Apr"; break; \
+ case 5: result += "May"; break; case 6: result += "Jun"; break; \
+ case 7: result += "Jul"; break; case 8: result += "Aug"; break; \
+ case 9: result += "Sep"; break; case 10: result += "Oct"; break; \
+ case 11: result += "Nov"; break; case 12: result += "Dec"; break; \
+ } \
+ result += '-'; \
+ result += std::to_string(d->day); \
+ return result; \
+ } \
+ }}
+MyDate{2024, 8, 23}
+// CHECK-NEXT: (MyType) My pretty printer!
+%quit
+
diff --git a/clang/tools/clang-repl/CMakeLists.txt b/clang/tools/clang-repl/CMakeLists.txt
index a35ff13494e115..0b09bbc74e5818 100644
--- a/clang/tools/clang-repl/CMakeLists.txt
+++ b/clang/tools/clang-repl/CMakeLists.txt
@@ -33,6 +33,14 @@ if(MSVC)
??_U at YAPEAX_K@Z
??_V at YAXPEAX@Z
??3 at YAXPEAX_K@Z
+ ??6?$basic_ostream at DU?$char_traits at D@std@@@std@@QEAAAEAV01 at H@Z
+ ??6?$basic_ostream at DU?$char_traits at D@std@@@std@@QEAAAEAV01 at M@Z
+ ??6?$basic_ostream at DU?$char_traits at D@std@@@std@@QEAAAEAV01 at N@Z
+ ??6?$basic_ostream at DU?$char_traits at D@std@@@std@@QEAAAEAV01 at PEBX@Z
+ ??6?$basic_ostream at DU?$char_traits at D@std@@@std@@QEAAAEAV01 at P6AAEAV01@AEAV01@@Z at Z
+ ??$?6U?$char_traits at D@std@@@std@@YAAEAV?$basic_ostream at DU?$char_traits at D@std@@@0 at AEAV10@D at Z
+ ??$?6U?$char_traits at D@std@@@std@@YAAEAV?$basic_ostream at DU?$char_traits at D@std@@@0 at AEAV10@PEBD at Z
+ ?_Facet_Register at std@@YAXPEAV_Facet_base at 1@@Z
)
else()
set(clang_repl_exports ${clang_repl_exports}
@@ -42,6 +50,14 @@ if(MSVC)
??_U at YAPAXI@Z
??_V at YAXPAX@Z
??_V at YAXPAXI@Z
+ ??6?$basic_ostream at DU?$char_traits at D@std@@@std@@QAEAAV01 at H@Z
+ ??6?$basic_ostream at DU?$char_traits at D@std@@@std@@QAEAAV01 at M@Z
+ ??6?$basic_ostream at DU?$char_traits at D@std@@@std@@QAEAAV01 at N@Z
+ ??6?$basic_ostream at DU?$char_traits at D@std@@@std@@QAEAAV01 at PBX@Z
+ ??6?$basic_ostream at DU?$char_traits at D@std@@@std@@QAEAAV01 at P6AAAV01@AAV01@@Z at Z
+ ??$?6U?$char_traits at D@std@@@std@@YAAAV?$basic_ostream at DU?$char_traits at D@std@@@0 at AAV10@D at Z
+ ??$?6U?$char_traits at D@std@@@std@@YAAAV?$basic_ostream at DU?$char_traits at D@std@@@0 at AAV10@PBD at Z
+ ?_Facet_Register at std@@YAXPAV_Facet_base at 1@@Z
)
endif()
diff --git a/clang/tools/clang-repl/ClangRepl.cpp b/clang/tools/clang-repl/ClangRepl.cpp
index 9cfc70462893dd..f4efb5e1b49eec 100644
--- a/clang/tools/clang-repl/ClangRepl.cpp
+++ b/clang/tools/clang-repl/ClangRepl.cpp
@@ -201,7 +201,6 @@ int main(int argc, const char **argv) {
DeviceCI->LoadRequestedPlugins();
std::unique_ptr<clang::Interpreter> Interp;
-
if (CudaEnabled) {
Interp = ExitOnErr(
clang::Interpreter::createWithCUDA(std::move(CI), std::move(DeviceCI)));
diff --git a/clang/unittests/Interpreter/CodeCompletionTest.cpp b/clang/unittests/Interpreter/CodeCompletionTest.cpp
index 72fcce76a1029d..23cfc469695d28 100644
--- a/clang/unittests/Interpreter/CodeCompletionTest.cpp
+++ b/clang/unittests/Interpreter/CodeCompletionTest.cpp
@@ -26,7 +26,7 @@ auto CB = clang::IncrementalCompilerBuilder();
class CodeCompletionTest : public InterpreterTestBase {
public:
- std::unique_ptr<Interpreter> Interp;
+ std::unique_ptr<clang::Interpreter> Interp;
void SetUp() override {
if (!HostSupportsJIT())
diff --git a/clang/unittests/Interpreter/InterpreterExtensionsTest.cpp b/clang/unittests/Interpreter/InterpreterExtensionsTest.cpp
index 5f1f29cebab148..85993e3e73e2c2 100644
--- a/clang/unittests/Interpreter/InterpreterExtensionsTest.cpp
+++ b/clang/unittests/Interpreter/InterpreterExtensionsTest.cpp
@@ -65,41 +65,13 @@ class InterpreterExtensionsTest : public InterpreterTestBase {
}
};
-class RecordRuntimeIBMetrics : public Interpreter {
- struct NoopRuntimeInterfaceBuilder : public RuntimeInterfaceBuilder {
- NoopRuntimeInterfaceBuilder(Sema &S) : S(S) {}
-
- TransformExprFunction *getPrintValueTransformer() override {
- TransformerQueries += 1;
- return &noop;
- }
-
- static ExprResult noop(RuntimeInterfaceBuilder *Builder, Expr *E,
- ArrayRef<Expr *> FixedArgs) {
- auto *B = static_cast<NoopRuntimeInterfaceBuilder *>(Builder);
- B->TransformedExprs += 1;
- return B->S.ActOnFinishFullExpr(E, /*DiscardedValue=*/false);
- }
-
- Sema &S;
- size_t TransformedExprs = 0;
- size_t TransformerQueries = 0;
- };
-
-public:
- // Inherit with using wouldn't make it public
- RecordRuntimeIBMetrics(std::unique_ptr<CompilerInstance> CI, llvm::Error &Err)
- : Interpreter(std::move(CI), Err) {}
-
- std::unique_ptr<RuntimeInterfaceBuilder> FindRuntimeInterface() override {
- assert(RuntimeIBPtr == nullptr && "We create the builder only once");
- Sema &S = getCompilerInstance()->getSema();
- auto RuntimeIB = std::make_unique<NoopRuntimeInterfaceBuilder>(S);
- RuntimeIBPtr = RuntimeIB.get();
- return RuntimeIB;
- }
-
- NoopRuntimeInterfaceBuilder *RuntimeIBPtr = nullptr;
+struct OutOfProcInterpreter : public Interpreter {
+ OutOfProcInterpreter(
+ std::unique_ptr<CompilerInstance> CI, llvm::Error &ErrOut,
+ std::unique_ptr<clang::ASTConsumer> Consumer,
+ std::unique_ptr<llvm::orc::LLJITBuilder> JITBuilder = nullptr)
+ : Interpreter(std::move(CI), ErrOut, std::move(JITBuilder),
+ std::move(Consumer)) {}
};
TEST_F(InterpreterExtensionsTest, FindRuntimeInterface) {
@@ -108,13 +80,23 @@ TEST_F(InterpreterExtensionsTest, FindRuntimeInterface) {
clang::IncrementalCompilerBuilder CB;
llvm::Error ErrOut = llvm::Error::success();
- RecordRuntimeIBMetrics Interp(cantFail(CB.CreateCpp()), ErrOut);
+ auto CI = cantFail(CB.CreateCpp());
+ // Do not attach the default consumer which is specialized for in-process.
+ class NoopConsumer : public ASTConsumer {};
+ std::unique_ptr<ASTConsumer> C = std::make_unique<NoopConsumer>();
+ OutOfProcInterpreter I(std::move(CI), ErrOut, std::move(C),
+ /*JITBuilder=*/nullptr);
cantFail(std::move(ErrOut));
- cantFail(Interp.Parse("int a = 1; a"));
- cantFail(Interp.Parse("int b = 2; b"));
- cantFail(Interp.Parse("int c = 3; c"));
- EXPECT_EQ(3U, Interp.RuntimeIBPtr->TransformedExprs);
- EXPECT_EQ(1U, Interp.RuntimeIBPtr->TransformerQueries);
+ cantFail(I.Parse("int a = 1; a"));
+ cantFail(I.Parse("int b = 2; b"));
+ cantFail(I.Parse("int c = 3; c"));
+
+ // Make sure no clang::Value logic is attached by the Interpreter.
+ Value V1;
+ llvm::cantFail(I.ParseAndExecute("int x = 42;"));
+ llvm::cantFail(I.ParseAndExecute("x", &V1));
+ EXPECT_FALSE(V1.isValid());
+ EXPECT_FALSE(V1.hasValue());
}
class CustomJBInterpreter : public Interpreter {
@@ -139,7 +121,7 @@ TEST_F(InterpreterExtensionsTest, DefaultCrossJIT) {
if (!IsARMTargetRegistered())
GTEST_SKIP();
- IncrementalCompilerBuilder CB;
+ clang::IncrementalCompilerBuilder CB;
CB.SetTargetTriple("armv6-none-eabi");
auto CI = cantFail(CB.CreateCpp());
llvm::Error ErrOut = llvm::Error::success();
@@ -153,7 +135,7 @@ TEST_F(InterpreterExtensionsTest, CustomCrossJIT) {
std::string TargetTriple = "armv6-none-eabi";
- IncrementalCompilerBuilder CB;
+ clang::IncrementalCompilerBuilder CB;
CB.SetTargetTriple(TargetTriple);
auto CI = cantFail(CB.CreateCpp());
>From 24d0867459582939915b4a8175bcc2f2f2aede33 Mon Sep 17 00:00:00 2001
From: Vassil Vassilev <v.g.vassilev at gmail.com>
Date: Sat, 31 Aug 2024 13:21:26 +0000
Subject: [PATCH 2/3] store
---
clang/include/clang/Interpreter/Interpreter.h | 2 +-
.../__clang_interpreter_runtime_printvalue.h | 28 +--
clang/lib/Interpreter/IncrementalExecutor.cpp | 2 +-
clang/lib/Interpreter/Interpreter.cpp | 2 +-
.../Interpreter/InterpreterValuePrinter.cpp | 205 ++++++++++++------
clang/test/Interpreter/pretty-print.c | 5 +-
clang/test/Interpreter/pretty-print.cpp | 2 +-
7 files changed, 158 insertions(+), 88 deletions(-)
diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h
index 195545a5b393b0..f394c8bf21e633 100644
--- a/clang/include/clang/Interpreter/Interpreter.h
+++ b/clang/include/clang/Interpreter/Interpreter.h
@@ -187,7 +187,7 @@ class Interpreter {
std::string ValueDataToString(const Value &V);
std::string ValueTypeToString(const Value &V) const;
- llvm::Expected<Expr *> AttachValuePrinting(Expr *E);
+ llvm::Expected<Expr *> convertExprToValue(Expr *E);
// When we deallocate clang::Value we need to run the destructor of the type.
// This function forces emission of the needed dtor.
diff --git a/clang/lib/Headers/__clang_interpreter_runtime_printvalue.h b/clang/lib/Headers/__clang_interpreter_runtime_printvalue.h
index 0a1367f970d0fb..38c20a5390387f 100644
--- a/clang/lib/Headers/__clang_interpreter_runtime_printvalue.h
+++ b/clang/lib/Headers/__clang_interpreter_runtime_printvalue.h
@@ -60,20 +60,20 @@ __DECL_PRINT_VALUE_RUNTIME(void); //
__DECL_PRINT_VALUE_RUNTIME(void *); //
__DECL_PRINT_VALUE_RUNTIME(char *const); //
__DECL_PRINT_VALUE_RUNTIME(char *); //
-__DECL_PRINT_VALUE_RUNTIME(bool);
-__DECL_PRINT_VALUE_RUNTIME(char);
-__DECL_PRINT_VALUE_RUNTIME(signed char);
-__DECL_PRINT_VALUE_RUNTIME(short);
-__DECL_PRINT_VALUE_RUNTIME(unsigned short);
-__DECL_PRINT_VALUE_RUNTIME(int);
-__DECL_PRINT_VALUE_RUNTIME(unsigned int);
-__DECL_PRINT_VALUE_RUNTIME(long);
-__DECL_PRINT_VALUE_RUNTIME(unsigned long);
-__DECL_PRINT_VALUE_RUNTIME(long long);
-__DECL_PRINT_VALUE_RUNTIME(unsigned long long);
-__DECL_PRINT_VALUE_RUNTIME(float);
-__DECL_PRINT_VALUE_RUNTIME(double);
-__DECL_PRINT_VALUE_RUNTIME(long double);
+// __DECL_PRINT_VALUE_RUNTIME(bool);
+// __DECL_PRINT_VALUE_RUNTIME(char);
+// __DECL_PRINT_VALUE_RUNTIME(signed char);
+// __DECL_PRINT_VALUE_RUNTIME(short);
+// __DECL_PRINT_VALUE_RUNTIME(unsigned short);
+// __DECL_PRINT_VALUE_RUNTIME(int);
+// __DECL_PRINT_VALUE_RUNTIME(unsigned int);
+// __DECL_PRINT_VALUE_RUNTIME(long);
+// __DECL_PRINT_VALUE_RUNTIME(unsigned long);
+// __DECL_PRINT_VALUE_RUNTIME(long long);
+// __DECL_PRINT_VALUE_RUNTIME(unsigned long long);
+// __DECL_PRINT_VALUE_RUNTIME(float);
+// __DECL_PRINT_VALUE_RUNTIME(double);
+// __DECL_PRINT_VALUE_RUNTIME(long double);
#endif
namespace __repl_runtime_detail {
diff --git a/clang/lib/Interpreter/IncrementalExecutor.cpp b/clang/lib/Interpreter/IncrementalExecutor.cpp
index 4d2adecaafce74..1824a5b4570a93 100644
--- a/clang/lib/Interpreter/IncrementalExecutor.cpp
+++ b/clang/lib/Interpreter/IncrementalExecutor.cpp
@@ -118,4 +118,4 @@ IncrementalExecutor::getSymbolAddress(llvm::StringRef Name,
return SymOrErr->getAddress();
}
-} // namespace clang
+} // end namespace clang
diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp
index 039ee4ecc99dd8..b4cf63f00e0bee 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -262,7 +262,7 @@ class InProcessPrintingASTConsumer final : public MultiplexConsumer {
if (auto *TLSD = llvm::dyn_cast<TopLevelStmtDecl>(D))
if (TLSD && TLSD->isSemiMissing()) {
auto ExprOrErr =
- Interp.AttachValuePrinting(cast<Expr>(TLSD->getStmt()));
+ Interp.convertExprToValue(cast<Expr>(TLSD->getStmt()));
if (llvm::Error E = ExprOrErr.takeError()) {
llvm::logAllUnhandledErrors(std::move(E), llvm::errs(),
"Value printing failed: ");
diff --git a/clang/lib/Interpreter/InterpreterValuePrinter.cpp b/clang/lib/Interpreter/InterpreterValuePrinter.cpp
index a622d681a96f84..e9269c4452da92 100644
--- a/clang/lib/Interpreter/InterpreterValuePrinter.cpp
+++ b/clang/lib/Interpreter/InterpreterValuePrinter.cpp
@@ -14,6 +14,7 @@
#include "InterpreterUtils.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/PrettyPrinter.h"
+#include "clang/AST/QualTypeNames.h"
#include "clang/AST/Type.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Interpreter/Interpreter.h"
@@ -26,8 +27,11 @@
#include "llvm/Support/raw_ostream.h"
#include <cassert>
+#include <cstdarg>
#include <string>
+#define DEBUG_TYPE "interp-value"
+
using namespace clang;
static std::string PrintDeclType(const QualType &QT, NamedDecl *D) {
@@ -202,20 +206,11 @@ static std::string PrintString(const char *const *Ptr, size_t N = 10000) {
}
// Build the CallExpr to `PrintValueRuntime`.
-static void BuildWrapperBody(Interpreter &Interp, Sema &S, ASTContext &Ctx,
- FunctionDecl *WrapperFD, QualType QT,
- const void *ValPtr) {
+static llvm::Error BuildWrapperBody(LookupResult &R, Sema &S,
+ ASTContext &Ctx, FunctionDecl *WrapperFD,
+ QualType QT, const void *ValPtr) {
Sema::SynthesizedFunctionScope SemaFScope(S, WrapperFD);
clang::DeclarationName RuntimeCallName;
- if (Ctx.getLangOpts().CPlusPlus)
- RuntimeCallName = S.PP.getIdentifierInfo("PrintValueRuntime");
- else
- RuntimeCallName =
- S.PP.getIdentifierInfo("caas__runtime__PrintValueRuntime");
-
- clang::LookupResult R(S, RuntimeCallName, SourceLocation(),
- clang::Sema::LookupOrdinaryName);
- S.LookupName(R, S.getCurScope());
Expr *OverldExpr = UnresolvedLookupExpr::Create(
Ctx, /*NamingClass=*/nullptr, NestedNameSpecifierLoc(),
@@ -254,22 +249,33 @@ static void BuildWrapperBody(Interpreter &Interp, Sema &S, ASTContext &Ctx,
ExprResult RuntimeCall =
S.ActOnCallExpr(S.getCurScope(), OverldExpr, SourceLocation(), CallArgs,
SourceLocation());
- assert(!RuntimeCall.isInvalid() && "Cannot create call to PrintValueRuntime");
+ if (RuntimeCall.isInvalid()) {
+ std::string Name = R.getLookupName().getAsString();
+ return llvm::make_error<llvm::StringError>(
+ "Cannot create call to " + Name, llvm::inconvertibleErrorCode());
+ }
// Create the ReturnStmt.
StmtResult RetStmt =
S.ActOnReturnStmt(SourceLocation(), RuntimeCall.get(), S.getCurScope());
- assert(!RetStmt.isInvalid() && "Cannot create ReturnStmt");
+
+ if (RetStmt.isInvalid())
+ return llvm::make_error<llvm::StringError>("Cannot create a return stmt",
+ llvm::inconvertibleErrorCode());
// Create the CompoundStmt.
StmtResult Body =
CompoundStmt::Create(Ctx, {RetStmt.get()}, FPOptionsOverride(),
SourceLocation(), SourceLocation());
- assert(!Body.isInvalid() && "Cannot create function body");
+ if (Body.isInvalid())
+ return llvm::make_error<llvm::StringError>("Cannot create function body",
+ llvm::inconvertibleErrorCode());
WrapperFD->setBody(Body.get());
// Add attribute `__attribute__((used))`.
WrapperFD->addAttr(UsedAttr::CreateImplicit(Ctx));
+
+ return llvm::Error::success();
}
static constexpr const char *const WrapperName = "__InterpreterCallPrint";
@@ -423,21 +429,42 @@ REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const char **Val) {
return PrintString(Val);
}
+// Check if the user has implemented a function that avoids fallback to
+// defaults.
+static clang::LookupResult LookupUserDefined(Sema &S, QualType QT) {
+ PrintingPolicy Policy = S.getASTContext().getPrintingPolicy();
+ Policy.SuppressElaboration = 1;
+ Policy.SuppressTagKeyword = 1;
+ Policy.FullyQualifiedName = 1;
+ std::string TypeStr = QT.getAsString(Policy);
+ DeclarationName Name = S.PP.getIdentifierInfo("caas_runtime_to_string_" + TypeStr);
+
+ LLVM_DEBUG(llvm::dbgs() << "Looking for user-defined '" << Name <<"'");
+
+ LookupResult R(S, Name, SourceLocation(),Sema::LookupOrdinaryName);
+ S.LookupName(R, S.getCurScope());
+ return R;
+}
+
namespace clang {
+
std::string Interpreter::ValueDataToString(const Value &V) {
+ Sema &S = getCompilerInstance()->getSema();
+ ASTContext &Ctx = S.getASTContext();
+
QualType QT = V.getType();
- QualType DesugaredTy = QT.getDesugaredType(V.getASTContext());
+ QualType DesugaredTy = QT.getDesugaredType(Ctx);
QualType NonRefTy = DesugaredTy.getNonReferenceType();
- if (NonRefTy->isEnumeralType())
- return PrintEnum(V);
+ if ((NonRefTy->isPointerType() || NonRefTy->isMemberPointerType()) &&
+ NonRefTy->getPointeeType()->isFunctionProtoType())
+ return PrintFunction(V, V.getPtr());
if (NonRefTy->isFunctionType())
return PrintFunction(V, &V);
- if ((NonRefTy->isPointerType() || NonRefTy->isMemberPointerType()) &&
- NonRefTy->getPointeeType()->isFunctionProtoType())
- return PrintFunction(V, V.getPtr());
+ if (NonRefTy->isEnumeralType())
+ return PrintEnum(V);
if (NonRefTy->isNullPtrType())
return "nullptr\n";
@@ -446,46 +473,52 @@ std::string Interpreter::ValueDataToString(const Value &V) {
if (auto *BT = DesugaredTy.getCanonicalType()->getAs<BuiltinType>()) {
switch (BT->getKind()) {
default:
- return "{ unknown builtin type: }" + std::to_string(BT->getKind());
-#define X(type, name) \
- case clang::BuiltinType::name: { \
- type val = V.get##name(); \
- return PrintValueRuntime(&val); \
- }
- REPL_BUILTIN_TYPES
-#undef X
+ return "{ error: unknown builtin type '" + std::to_string(BT->getKind()) +" '}";
+ case clang::BuiltinType::Bool: { bool val = V.getBool(); return PrintValueRuntime(&val); } case clang::BuiltinType::Char_S: { char val = V.getChar_S(); return PrintValueRuntime(&val); } case clang::BuiltinType::SChar: { signed char val = V.getSChar(); return PrintValueRuntime(&val); } case clang::BuiltinType::Char_U: { unsigned char val = V.getChar_U(); return PrintValueRuntime(&val); } case clang::BuiltinType::UChar: { unsigned char val = V.getUChar(); return PrintValueRuntime(&val); } case clang::BuiltinType::Short: { short val = V.getShort(); return PrintValueRuntime(&val); } case clang::BuiltinType::UShort: { unsigned short val = V.getUShort(); return PrintValueRuntime(&val); } case clang::BuiltinType::Int: { int val = V.getInt(); return PrintValueRuntime(&val); } case clang::BuiltinType::UInt: { unsigned int val = V.getUInt(); return PrintValueRuntime(&val); } case clang::BuiltinType::Long: { long val = V.getLong(); return PrintValueRuntime(&val); } case clang::BuiltinType::ULong: { unsigned long val = V.getULong(); return PrintValueRuntime(&val); } case clang::BuiltinType::LongLong: { long long val = V.getLongLong(); return PrintValueRuntime(&val); } case clang::BuiltinType::ULongLong: { unsigned long long val = V.getULongLong(); return PrintValueRuntime(&val); } case clang::BuiltinType::Float: { float val = V.getFloat(); return PrintValueRuntime(&val); } case clang::BuiltinType::Double: { double val = V.getDouble(); return PrintValueRuntime(&val); } case clang::BuiltinType::LongDouble: { long double val = V.getLongDouble(); return PrintValueRuntime(&val); }
}
}
if (auto *CXXRD = NonRefTy->getAsCXXRecordDecl())
if (CXXRD->isLambda())
return PrintAddress(V.getPtr(), '@');
- // All fails then generate a runtime call, this is slow.
- Sema &S = getCompilerInstance()->getSema();
- ASTContext &Ctx = S.getASTContext();
+ // FIXME: Add support for custom printers in C.
+ if (NonRefTy->isPointerType()) {
+ if (NonRefTy->getPointeeType()->isCharType())
+ return PrintValueRuntime((const char**)V.getPtrAddress());
+ return PrintValueRuntime(V.getPtr());
+ }
- QualType RetTy;
- if (Ctx.getLangOpts().CPlusPlus && !StdString) {
+ QualType RetTy = Ctx.getPointerType(Ctx.CharTy.withConst());
- // Only include the header on demand because it's very heavy.
- if (llvm::Error E = ParseAndExecute(
- "#include <__clang_interpreter_runtime_printvalue.h>")) {
- llvm::logAllUnhandledErrors(std::move(E), llvm::errs(), "Parsing failed");
- return "{Internal error}";
- }
+ LookupResult R = LookupUserDefined(S, QT);
+ if (!R.isSingleResult())
+ return "{error: multiple results for '" + R.getLookupName().getAsString() + "'}";
- // Find and cache std::string.
- NamespaceDecl *Std = LookupNamespace(S, "std");
- assert(Std && "Cannot find namespace std");
- Decl *StdStringDecl = LookupNamed(S, "string", Std);
- assert(StdStringDecl && "Cannot find std::string");
- const auto *StdStringTyDecl = llvm::dyn_cast<TypeDecl>(StdStringDecl);
- assert(StdStringTyDecl && "Cannot find type of std::string");
- RetTy = QualType(StdStringTyDecl->getTypeForDecl(), /*Quals=*/0);
- } else {
- RetTy = Ctx.getPointerType(Ctx.CharTy.withConst());
+ if (R.empty()) {
+
+ if (!Ctx.getLangOpts().CPlusPlus)
+ return PrintValueRuntime(V.getPtr());
+
+ if (S.StdNamespace && !StdString) {
+
+ // Only include the header on demand because it's very heavy.
+ auto TUorE = Parse("#include <__clang_interpreter_runtime_printvalue.h>");
+ if (llvm::Error E = TUorE.takeError() ) {
+ llvm::logAllUnhandledErrors(std::move(E), llvm::errs(), "Parsing failed");
+ return "{error: cannot parse system header}";
+ }
+
+ // Find and cache std::string.
+ Decl *StdStringDecl = LookupNamed(S, "string", S.getStdNamespace());
+ assert(StdStringDecl && "Cannot find std::string");
+ const auto *StdStringTyDecl = llvm::dyn_cast<TypeDecl>(StdStringDecl);
+ assert(StdStringTyDecl && "Cannot find type of std::string");
+ RetTy = QualType(StdStringTyDecl->getTypeForDecl(), /*Quals=*/0);
+ }
}
+ // All fails then generate a runtime call, this is slow.
+
// Create the wrapper function.
DeclarationName DeclName = &Ctx.Idents.get(CreateUniqName(WrapperName));
QualType FnTy =
@@ -501,7 +534,30 @@ std::string Interpreter::ValueDataToString(const Value &V) {
if (!V.isManuallyAlloc())
ValPtr = V.getPtrAddress();
- BuildWrapperBody(*this, S, Ctx, WrapperFD, V.getType(), ValPtr);
+ // // Check if the user has implemented a function that avoids fallback to
+ // // defaults.
+ // if (Ctx.getLangOpts().CPlusPlus) {
+ // RuntimeCallName = S.PP.getIdentifierInfo("PrintValueRuntime");
+ // } else {
+ // PrintingPolicy Policy = Ctx.getPrintingPolicy();
+ // Policy.SuppressElaboration = 1;
+ // Policy.SuppressTagKeyword = 1;
+ // Policy.FullyQualifiedName = 1;
+ // std::string TypeStr = QT.getAsString(Policy);
+ // printf("KKK %s\n", TypeStr.c_str());
+ // // clang::TypeName::getFullyQualifiedName(QT, Ctx, Policy);
+ // RuntimeCallName =
+ // S.PP.getIdentifierInfo("caas__runtime__PrintValueRuntime__" + TypeStr);
+ // }
+
+ // LookupResult R(S, RuntimeCallName, SourceLocation(),Sema::LookupOrdinaryName);
+ // S.LookupName(R, S.getCurScope());
+
+ if (llvm::Error E = BuildWrapperBody(R, S, Ctx, WrapperFD, V.getType(), ValPtr)) {
+ llvm::logAllUnhandledErrors(std::move(E), llvm::errs(),
+ "Fail to build a wrapper function");
+ return "{Unable to print the value!}";
+ }
auto AddrOrErr = CompileDecl(*this, WrapperFD);
if (!AddrOrErr)
@@ -509,7 +565,7 @@ std::string Interpreter::ValueDataToString(const Value &V) {
"Fail to get symbol address");
if (auto *Main = AddrOrErr->toPtr<std::string (*)()>())
return (*Main)();
- return "Unable to print the value!";
+ return "{Unable to print the value!}";
}
std::string Interpreter::ValueTypeToString(const Value &V) const {
@@ -564,7 +620,12 @@ class InterfaceKindVisitor
}
InterfaceKind VisitRecordType(const RecordType *Ty) {
- return InterfaceKind::WithAlloc;
+ if (S.getLangOpts().CPlusPlus)
+ return InterfaceKind::WithAlloc;
+ ExprResult AddrOfE = S.CreateBuiltinUnaryOp(SourceLocation(), UO_AddrOf, E->IgnoreImpCasts());
+ assert(!AddrOfE.isInvalid() && "Can not create unary expression");
+ Args.push_back(AddrOfE.get());
+ return InterfaceKind::NoAlloc;
}
InterfaceKind VisitMemberPointerType(const MemberPointerType *Ty) {
@@ -635,9 +696,14 @@ class InterfaceKindVisitor
}
};
+static constexpr llvm::StringRef VPName[] = {
+ "__clang_Interpreter_SetValueNoAlloc",
+ "__clang_Interpreter_SetValueWithAlloc",
+ "__clang_Interpreter_SetValueCopyArr", "__ci_newtag"};
+
// This synthesizes a call expression to a speciall
// function that is responsible for generating the Value.
-// In general, we transform:
+// In general, we transform c++:
// clang-repl> x
// To:
// // 1. If x is a built-in type like int, float.
@@ -648,7 +714,7 @@ class InterfaceKindVisitor
// // 3. If x is a struct, but a rvalue.
// new (__clang_Interpreter_SetValueWithAlloc(ThisInterp, OpaqueValue,
// xQualType)) (x);
-llvm::Expected<Expr *> Interpreter::AttachValuePrinting(Expr *E) {
+llvm::Expected<Expr *> Interpreter::convertExprToValue(Expr *E) {
Sema &S = getCompilerInstance()->getSema();
ASTContext &Ctx = S.getASTContext();
@@ -670,23 +736,19 @@ llvm::Expected<Expr *> Interpreter::AttachValuePrinting(Expr *E) {
Interface = S.BuildDeclarationNameExpr(CSS, R, /*ADL=*/false).get();
return llvm::Error::success();
};
- static constexpr llvm::StringRef Builtin[] = {
- "__clang_Interpreter_SetValueNoAlloc",
- "__clang_Interpreter_SetValueWithAlloc",
- "__clang_Interpreter_SetValueCopyArr", "__ci_newtag"};
if (llvm::Error Err =
- LookupInterface(ValuePrintingInfo[NoAlloc], Builtin[NoAlloc]))
+ LookupInterface(ValuePrintingInfo[NoAlloc], VPName[NoAlloc]))
return std::move(Err);
if (Ctx.getLangOpts().CPlusPlus) {
if (llvm::Error Err =
- LookupInterface(ValuePrintingInfo[WithAlloc], Builtin[WithAlloc]))
+ LookupInterface(ValuePrintingInfo[WithAlloc], VPName[WithAlloc]))
return std::move(Err);
if (llvm::Error Err =
- LookupInterface(ValuePrintingInfo[CopyArray], Builtin[CopyArray]))
+ LookupInterface(ValuePrintingInfo[CopyArray], VPName[CopyArray]))
return std::move(Err);
if (llvm::Error Err =
- LookupInterface(ValuePrintingInfo[NewTag], Builtin[NewTag]))
+ LookupInterface(ValuePrintingInfo[NewTag], VPName[NewTag]))
return std::move(Err);
}
}
@@ -725,6 +787,8 @@ llvm::Expected<Expr *> Interpreter::AttachValuePrinting(Expr *E) {
Scope *Scope = nullptr;
ExprResult SetValueE;
InterfaceKind Kind = V.computeInterfaceKind(DesugaredTy);
+ if (!Ctx.getLangOpts().CPlusPlus && Kind == InterfaceKind::WithAlloc)
+ Kind = InterfaceKind::NoAlloc;
switch (Kind) {
case InterfaceKind::WithAlloc:
LLVM_FALLTHROUGH;
@@ -733,7 +797,9 @@ llvm::Expected<Expr *> Interpreter::AttachValuePrinting(Expr *E) {
ExprResult AllocCall =
S.ActOnCallExpr(Scope, ValuePrintingInfo[InterfaceKind::WithAlloc],
E->getBeginLoc(), AdjustedArgs, E->getEndLoc());
- assert(!AllocCall.isInvalid() && "Can't create runtime interface call!");
+ if (AllocCall.isInvalid())
+ return llvm::make_error<llvm::StringError>("Cannot call to " + VPName[WithAlloc],
+ llvm::inconvertibleErrorCode());
TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ty, SourceLocation());
@@ -755,6 +821,11 @@ llvm::Expected<Expr *> Interpreter::AttachValuePrinting(Expr *E) {
SetValueE =
S.ActOnCallExpr(Scope, ValuePrintingInfo[InterfaceKind::CopyArray],
SourceLocation(), Args, SourceLocation());
+ if (SetValueE.isInvalid())
+ return llvm::make_error<llvm::StringError>("Cannot call to " + VPName[CopyArray],
+ llvm::inconvertibleErrorCode());
+ break;
+ //return SetValueE.get();
}
Expr *Args[] = {AllocCall.get(), ValuePrintingInfo[InterfaceKind::NewTag]};
ExprResult CXXNewCall = S.BuildCXXNew(
@@ -764,8 +835,10 @@ llvm::Expected<Expr *> Interpreter::AttachValuePrinting(Expr *E) {
/*TypeIdParens=*/SourceRange(), TSI->getType(), TSI, std::nullopt,
E->getSourceRange(), E);
- assert(!CXXNewCall.isInvalid() &&
- "Can't create runtime placement new call!");
+ if (CXXNewCall.isInvalid())
+ return llvm::make_error<llvm::StringError>(
+ "Cannot build a call to placement new",
+ llvm::inconvertibleErrorCode());
SetValueE = S.ActOnFinishFullExpr(CXXNewCall.get(),
/*DiscardedValue=*/false);
diff --git a/clang/test/Interpreter/pretty-print.c b/clang/test/Interpreter/pretty-print.c
index 927cd836ab693b..63e63631640a3a 100644
--- a/clang/test/Interpreter/pretty-print.c
+++ b/clang/test/Interpreter/pretty-print.c
@@ -37,9 +37,6 @@ double d = 4.21; d
struct S1{} s1; s1
// CHECK-NEXT: (S1 &) @0x{{[0-9a-f]+}}
-S1{}
-// CHECK-NEXT: (S1) @0x{{[0-9a-f]+}}
-
struct S2 {int d;} E = {22}; E
// CHECK-NEXT: (struct S2 &) @0x{{[0-9a-f]+}}
E.d
@@ -62,7 +59,7 @@ const char * caas__runtime__PrintValueRuntime(const struct ConstLiteral *) { \
struct ConstLiteral CL; CL
// CHECK-NEXT: ConstLiteral
-struct Point{int x; int y};
+struct Point{int x; int y;};
const char * caas__runtime__PrintValueRuntime(const struct Point *p) { \
char[11 + 11 + 4 + 1] result; \
sprintf(result, "(%d, %d)", p->x, p->y); \
diff --git a/clang/test/Interpreter/pretty-print.cpp b/clang/test/Interpreter/pretty-print.cpp
index b7a6c07c481dd3..87f595fd03360b 100644
--- a/clang/test/Interpreter/pretty-print.cpp
+++ b/clang/test/Interpreter/pretty-print.cpp
@@ -165,6 +165,6 @@ namespace caas { namespace runtime { \
} \
}}
MyDate{2024, 8, 23}
-// CHECK-NEXT: (MyType) My pretty printer!
+// CHECK-NEXT: (MyDate) 2024-Aug-23
%quit
>From 9554375738fcfaebab36e41f57618dafc5d7264c Mon Sep 17 00:00:00 2001
From: Vassil Vassilev <v.g.vassilev at gmail.com>
Date: Fri, 6 Sep 2024 09:00:11 +0000
Subject: [PATCH 3/3] store
---
clang/include/clang/Interpreter/Interpreter.h | 2 +
clang/include/clang/Interpreter/Value.h | 2 +-
clang/lib/Interpreter/InterpreterUtils.h | 2 +-
.../Interpreter/InterpreterValuePrinter.cpp | 511 ++++++++----------
clang/test/Interpreter/pretty-print.cpp | 8 +-
clang/tools/clang-repl/ClangRepl.cpp | 2 +-
6 files changed, 239 insertions(+), 288 deletions(-)
diff --git a/clang/include/clang/Interpreter/Interpreter.h b/clang/include/clang/Interpreter/Interpreter.h
index f394c8bf21e633..1763afd240a2c4 100644
--- a/clang/include/clang/Interpreter/Interpreter.h
+++ b/clang/include/clang/Interpreter/Interpreter.h
@@ -41,6 +41,7 @@ class CXXRecordDecl;
class Decl;
class IncrementalExecutor;
class IncrementalParser;
+class LookupResult;
/// Create a pre-configured \c CompilerInstance for incremental processing.
class IncrementalCompilerBuilder {
@@ -186,6 +187,7 @@ class Interpreter {
std::string ValueDataToString(const Value &V);
std::string ValueTypeToString(const Value &V) const;
+ std::string CallUserSpecifiedPrinter(LookupResult &R, const Value &V);
llvm::Expected<Expr *> convertExprToValue(Expr *E);
diff --git a/clang/include/clang/Interpreter/Value.h b/clang/include/clang/Interpreter/Value.h
index 5417c550e7f985..664f9e9008603e 100644
--- a/clang/include/clang/Interpreter/Value.h
+++ b/clang/include/clang/Interpreter/Value.h
@@ -178,7 +178,7 @@ class REPL_EXTERNAL_VISIBILITY Value {
template <typename T> struct convertFwd {
static T cast(const Value &V) {
if (V.isPointerOrObjectType())
- return (T)(uintptr_t)V.as<void *>();
+ return *(T*)(uintptr_t)V.as<void *>();
if (!V.isValid() || V.isVoid()) {
return T();
}
diff --git a/clang/lib/Interpreter/InterpreterUtils.h b/clang/lib/Interpreter/InterpreterUtils.h
index 4b92cf0c4bf978..bd23f249551b5f 100644
--- a/clang/lib/Interpreter/InterpreterUtils.h
+++ b/clang/lib/Interpreter/InterpreterUtils.h
@@ -45,7 +45,7 @@ NamespaceDecl *LookupNamespace(Sema &S, llvm::StringRef Name,
const DeclContext *Within = nullptr);
NamedDecl *LookupNamed(Sema &S, llvm::StringRef Name,
- const DeclContext *Within);
+ const DeclContext *Within = nullptr);
NestedNameSpecifier *CreateNestedNameSpecifier(const ASTContext &Ctx,
const NamespaceDecl *Namesp);
diff --git a/clang/lib/Interpreter/InterpreterValuePrinter.cpp b/clang/lib/Interpreter/InterpreterValuePrinter.cpp
index e9269c4452da92..df2ee3edcc1563 100644
--- a/clang/lib/Interpreter/InterpreterValuePrinter.cpp
+++ b/clang/lib/Interpreter/InterpreterValuePrinter.cpp
@@ -28,13 +28,14 @@
#include <cassert>
#include <cstdarg>
+#include <sstream>
#include <string>
#define DEBUG_TYPE "interp-value"
using namespace clang;
-static std::string PrintDeclType(const QualType &QT, NamedDecl *D) {
+static std::string DeclTypeToString(const QualType &QT, NamedDecl *D) {
std::string Str;
llvm::raw_string_ostream SS(Str);
if (QT.hasQualifiers())
@@ -43,7 +44,7 @@ static std::string PrintDeclType(const QualType &QT, NamedDecl *D) {
return Str;
}
-static std::string PrintQualType(ASTContext &Ctx, QualType QT) {
+static std::string QualTypeToString(ASTContext &Ctx, QualType QT) {
PrintingPolicy Policy(Ctx.getPrintingPolicy());
// Print the Allocator in STL containers, for instance.
Policy.SuppressDefaultTemplateArgs = false;
@@ -65,10 +66,10 @@ static std::string PrintQualType(ASTContext &Ctx, QualType QT) {
const QualType NonRefTy = QT.getNonReferenceType();
if (const auto *TTy = llvm::dyn_cast<TagType>(NonRefTy))
- return PrintDeclType(NonRefTy, TTy->getDecl());
+ return DeclTypeToString(NonRefTy, TTy->getDecl());
if (const auto *TRy = dyn_cast<RecordType>(NonRefTy))
- return PrintDeclType(NonRefTy, TRy->getDecl());
+ return DeclTypeToString(NonRefTy, TRy->getDecl());
const QualType Canon = NonRefTy.getCanonicalType();
@@ -90,12 +91,12 @@ static std::string PrintQualType(ASTContext &Ctx, QualType QT) {
return GetFullTypeName(Ctx, Canon);
else if (llvm::isa<TemplateSpecializationType>(SSDesugar))
return GetFullTypeName(Ctx, NonRefTy);
- return PrintDeclType(NonRefTy, TDTy->getDecl());
+ return DeclTypeToString(NonRefTy, TDTy->getDecl());
}
return GetFullTypeName(Ctx, NonRefTy);
}
-static std::string PrintEnum(const Value &V) {
+static std::string EnumToString(const Value &V) {
std::string Str;
llvm::raw_string_ostream SS(Str);
ASTContext &Ctx = const_cast<ASTContext &>(V.getASTContext());
@@ -119,11 +120,11 @@ static std::string PrintEnum(const Value &V) {
}
llvm::SmallString<64> APStr;
AP.toString(APStr, /*Radix=*/10);
- SS << " : " << PrintQualType(Ctx, ED->getIntegerType()) << " " << APStr;
+ SS << " : " << QualTypeToString(Ctx, ED->getIntegerType()) << " " << APStr;
return Str;
}
-static std::string PrintFunction(const Value &V, const void *Ptr) {
+static std::string FunctionToString(const Value &V, const void *Ptr) {
std::string Str;
llvm::raw_string_ostream SS(Str);
SS << "Function @" << Ptr;
@@ -155,7 +156,7 @@ static std::string PrintFunction(const Value &V, const void *Ptr) {
return Str;
}
-static std::string PrintAddress(const void *Ptr, char Prefix) {
+static std::string AddressToString(const void *Ptr, char Prefix) {
std::string Str;
llvm::raw_string_ostream SS(Str);
if (!Ptr)
@@ -164,51 +165,10 @@ static std::string PrintAddress(const void *Ptr, char Prefix) {
return Str;
}
-// FIXME: Encodings. Handle unprintable characters such as control characters.
-static std::string PrintOneChar(char Val) {
- std::string Str;
- llvm::raw_string_ostream SS(Str);
-
- SS << "'" << Val << "'";
- return Str;
-}
-
-// Char pointers
-// Assumption is this is a string.
-// N is limit to prevent endless loop if Ptr is not really a string.
-static std::string PrintString(const char *const *Ptr, size_t N = 10000) {
- std::string Str;
- llvm::raw_string_ostream SS(Str);
-
- const char *Start = *Ptr;
- if (!Start)
- return "nullptr";
-
- const char *End = Start + N;
- // If we're gonna do this, better make sure the end is valid too
- // FIXME: getpagesize() & GetSystemInfo().dwPageSize might be better
- static constexpr auto PAGE_SIZE = 1024;
- while (N > 1024) {
- N -= PAGE_SIZE;
- End = Start + N;
- }
-
- if (*Start == 0)
- return "\"\"";
-
- // Copy the bytes until we get a null-terminator
- SS << "\"";
- while (Start < End && *Start)
- SS << *Start++;
- SS << "\"";
-
- return Str;
-}
-
// Build the CallExpr to `PrintValueRuntime`.
-static llvm::Error BuildWrapperBody(LookupResult &R, Sema &S,
- ASTContext &Ctx, FunctionDecl *WrapperFD,
- QualType QT, const void *ValPtr) {
+static llvm::Error BuildWrapperBody(LookupResult &R, Sema &S, ASTContext &Ctx,
+ FunctionDecl *WrapperFD, QualType QT,
+ const void *ValPtr) {
Sema::SynthesizedFunctionScope SemaFScope(S, WrapperFD);
clang::DeclarationName RuntimeCallName;
@@ -251,8 +211,8 @@ static llvm::Error BuildWrapperBody(LookupResult &R, Sema &S,
SourceLocation());
if (RuntimeCall.isInvalid()) {
std::string Name = R.getLookupName().getAsString();
- return llvm::make_error<llvm::StringError>(
- "Cannot create call to " + Name, llvm::inconvertibleErrorCode());
+ return llvm::make_error<llvm::StringError>("Cannot create call to " + Name,
+ llvm::inconvertibleErrorCode());
}
// Create the ReturnStmt.
@@ -313,212 +273,81 @@ static std::string CreateUniqName(std::string Base) {
return Base;
}
-REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const void *Ptr) {
- return PrintAddress(Ptr, '@');
-}
-
-REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const void **Ptr) {
- return PrintAddress(*Ptr, '@');
-}
-
-REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const bool *Val) {
- if (*Val)
- return "true";
- return "false";
-}
-
-REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const char *Val) {
- return PrintOneChar(*Val);
-}
-
-REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const signed char *Val) {
- return PrintOneChar(*Val);
-}
-
-REPL_EXTERNAL_VISIBILITY std::string
-PrintValueRuntime(const unsigned char *Val) {
- return PrintOneChar(*Val);
-}
-
-REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const short *Val) {
- std::string Str;
- llvm::raw_string_ostream SS(Str);
- SS << *Val;
- return Str;
-}
-
-REPL_EXTERNAL_VISIBILITY
-std::string PrintValueRuntime(const unsigned short *Val) {
- std::string Str;
- llvm::raw_string_ostream SS(Str);
- SS << *Val;
- return Str;
-}
-
-REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const int *Val) {
- std::string Str;
- llvm::raw_string_ostream SS(Str);
- SS << *Val;
- return Str;
-}
-
-REPL_EXTERNAL_VISIBILITY
-std::string PrintValueRuntime(const unsigned int *Val) {
- std::string Str;
- llvm::raw_string_ostream SS(Str);
- SS << *Val;
- return Str;
-}
-
-REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const long *Val) {
- std::string Str;
- llvm::raw_string_ostream SS(Str);
- SS << *Val;
- return Str;
-}
-
-REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const long long *Val) {
- std::string Str;
- llvm::raw_string_ostream SS(Str);
- SS << *Val;
- return Str;
-}
-
-REPL_EXTERNAL_VISIBILITY
-std::string PrintValueRuntime(const unsigned long *Val) {
- std::string Str;
- llvm::raw_string_ostream SS(Str);
- SS << *Val;
- return Str;
-}
-
-REPL_EXTERNAL_VISIBILITY
-std::string PrintValueRuntime(const unsigned long long *Val) {
- std::string Str;
- llvm::raw_string_ostream SS(Str);
- SS << *Val;
- return Str;
-}
-
-REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const float *Val) {
- std::string Str;
- llvm::raw_string_ostream SS(Str);
- SS << llvm::format("%#.6g", *Val) << 'f';
- return Str;
-}
-
-REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const double *Val) {
- std::string Str;
- llvm::raw_string_ostream SS(Str);
- SS << llvm::format("%#.12g", *Val);
- return Str;
-}
-
-REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const long double *Val) {
- std::string Str;
- llvm::raw_string_ostream SS(Str);
- SS << llvm::format("%#.8Lg", *Val) << 'L';
- return Str;
-}
-
-REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const char *const *Val) {
- return PrintString(Val);
-}
-
-REPL_EXTERNAL_VISIBILITY std::string PrintValueRuntime(const char **Val) {
- return PrintString(Val);
-}
-
// Check if the user has implemented a function that avoids fallback to
// defaults.
static clang::LookupResult LookupUserDefined(Sema &S, QualType QT) {
- PrintingPolicy Policy = S.getASTContext().getPrintingPolicy();
- Policy.SuppressElaboration = 1;
- Policy.SuppressTagKeyword = 1;
- Policy.FullyQualifiedName = 1;
- std::string TypeStr = QT.getAsString(Policy);
- DeclarationName Name = S.PP.getIdentifierInfo("caas_runtime_to_string_" + TypeStr);
-
- LLVM_DEBUG(llvm::dbgs() << "Looking for user-defined '" << Name <<"'");
-
- LookupResult R(S, Name, SourceLocation(),Sema::LookupOrdinaryName);
+ DeclarationName Name;
+ ASTContext &Ctx = S.getASTContext();
+ if (S.getLangOpts().CPlusPlus) {
+ if (auto *NSD = dyn_cast_or_null<NamespaceDecl>(LookupNamed(S, "caas")))
+ if (NamedDecl* RtD = LookupNamed(S, "runtime", NSD)) {
+ Name = S.PP.getIdentifierInfo("to_string");
+ LookupResult R(S, Name, SourceLocation(), Sema::LookupOrdinaryName);
+ S.LookupQualifiedName(R, cast<DeclContext>(RtD)->getPrimaryContext());
+ LookupResult::Filter F = R.makeFilter();
+ while (F.hasNext()) {
+ NamedDecl* D = F.next();
+ FunctionDecl *FD = dyn_cast<FunctionDecl>(D);
+ if (!FD && isa<FunctionTemplateDecl>(D))
+ FD = cast<FunctionTemplateDecl>(D)->getTemplatedDecl();
+ if (FD)
+ if (FD->getNumParams() == 1) {
+ QualType ParamTy = FD->getParamDecl(0)->getType();
+ if (ParamTy->isPointerOrReferenceType()) {
+ // It is a uninstantiated template, we can't really match types.
+ if (isa<FunctionTemplateDecl>(D))
+ continue;
+ ParamTy = ParamTy->getPointeeType();
+ if (Ctx.hasSameUnqualifiedType(ParamTy, QT))
+ continue;
+ }
+ }
+
+ F.erase();
+ }
+ F.done();
+ return R;
+ }
+ } else {
+ PrintingPolicy Policy = S.getASTContext().getPrintingPolicy();
+ Policy.SuppressElaboration = 1;
+ Policy.SuppressTagKeyword = 1;
+ Policy.FullyQualifiedName = 1;
+ std::string TypeStr = QT.getAsString(Policy);
+ Name = S.PP.getIdentifierInfo("caas_runtime_to_string_" + TypeStr);
+
+ LLVM_DEBUG(llvm::dbgs() << "Looking for user-defined '" << Name << "'");
+ }
+ LookupResult R(S, Name, SourceLocation(), Sema::LookupOrdinaryName);
S.LookupName(R, S.getCurScope());
return R;
}
namespace clang {
-std::string Interpreter::ValueDataToString(const Value &V) {
+std::string Interpreter::CallUserSpecifiedPrinter(LookupResult &R, const Value &V) {
+ assert(!R.empty());
+ // if (!R.isSingleResult())
+ // return "{error: multiple results for '" + R.getLookupName().getAsString() +
+ // "'}";
+
Sema &S = getCompilerInstance()->getSema();
ASTContext &Ctx = S.getASTContext();
- QualType QT = V.getType();
- QualType DesugaredTy = QT.getDesugaredType(Ctx);
- QualType NonRefTy = DesugaredTy.getNonReferenceType();
-
- if ((NonRefTy->isPointerType() || NonRefTy->isMemberPointerType()) &&
- NonRefTy->getPointeeType()->isFunctionProtoType())
- return PrintFunction(V, V.getPtr());
-
- if (NonRefTy->isFunctionType())
- return PrintFunction(V, &V);
-
- if (NonRefTy->isEnumeralType())
- return PrintEnum(V);
-
- if (NonRefTy->isNullPtrType())
- return "nullptr\n";
-
- // If it is a builtin type dispatch to the builtin overloads.
- if (auto *BT = DesugaredTy.getCanonicalType()->getAs<BuiltinType>()) {
- switch (BT->getKind()) {
- default:
- return "{ error: unknown builtin type '" + std::to_string(BT->getKind()) +" '}";
- case clang::BuiltinType::Bool: { bool val = V.getBool(); return PrintValueRuntime(&val); } case clang::BuiltinType::Char_S: { char val = V.getChar_S(); return PrintValueRuntime(&val); } case clang::BuiltinType::SChar: { signed char val = V.getSChar(); return PrintValueRuntime(&val); } case clang::BuiltinType::Char_U: { unsigned char val = V.getChar_U(); return PrintValueRuntime(&val); } case clang::BuiltinType::UChar: { unsigned char val = V.getUChar(); return PrintValueRuntime(&val); } case clang::BuiltinType::Short: { short val = V.getShort(); return PrintValueRuntime(&val); } case clang::BuiltinType::UShort: { unsigned short val = V.getUShort(); return PrintValueRuntime(&val); } case clang::BuiltinType::Int: { int val = V.getInt(); return PrintValueRuntime(&val); } case clang::BuiltinType::UInt: { unsigned int val = V.getUInt(); return PrintValueRuntime(&val); } case clang::BuiltinType::Long: { long val = V.getLong(); return PrintValueRuntime(&val); } case clang::BuiltinType::ULong: { unsigned long val = V.getULong(); return PrintValueRuntime(&val); } case clang::BuiltinType::LongLong: { long long val = V.getLongLong(); return PrintValueRuntime(&val); } case clang::BuiltinType::ULongLong: { unsigned long long val = V.getULongLong(); return PrintValueRuntime(&val); } case clang::BuiltinType::Float: { float val = V.getFloat(); return PrintValueRuntime(&val); } case clang::BuiltinType::Double: { double val = V.getDouble(); return PrintValueRuntime(&val); } case clang::BuiltinType::LongDouble: { long double val = V.getLongDouble(); return PrintValueRuntime(&val); }
- }
- }
- if (auto *CXXRD = NonRefTy->getAsCXXRecordDecl())
- if (CXXRD->isLambda())
- return PrintAddress(V.getPtr(), '@');
-
- // FIXME: Add support for custom printers in C.
- if (NonRefTy->isPointerType()) {
- if (NonRefTy->getPointeeType()->isCharType())
- return PrintValueRuntime((const char**)V.getPtrAddress());
- return PrintValueRuntime(V.getPtr());
- }
-
+ // Check the found candidates.
QualType RetTy = Ctx.getPointerType(Ctx.CharTy.withConst());
-
- LookupResult R = LookupUserDefined(S, QT);
- if (!R.isSingleResult())
- return "{error: multiple results for '" + R.getLookupName().getAsString() + "'}";
-
- if (R.empty()) {
-
- if (!Ctx.getLangOpts().CPlusPlus)
- return PrintValueRuntime(V.getPtr());
-
- if (S.StdNamespace && !StdString) {
-
- // Only include the header on demand because it's very heavy.
- auto TUorE = Parse("#include <__clang_interpreter_runtime_printvalue.h>");
- if (llvm::Error E = TUorE.takeError() ) {
- llvm::logAllUnhandledErrors(std::move(E), llvm::errs(), "Parsing failed");
- return "{error: cannot parse system header}";
- }
-
- // Find and cache std::string.
- Decl *StdStringDecl = LookupNamed(S, "string", S.getStdNamespace());
- assert(StdStringDecl && "Cannot find std::string");
- const auto *StdStringTyDecl = llvm::dyn_cast<TypeDecl>(StdStringDecl);
- assert(StdStringTyDecl && "Cannot find type of std::string");
- RetTy = QualType(StdStringTyDecl->getTypeForDecl(), /*Quals=*/0);
+ if (Ctx.getLangOpts().CPlusPlus) {
+ if (!S.StdNamespace)
+ return "{error: user-defined pretty printers require std::string; consider #include <string> }";
+ // Find and cache std::string.
+ if (S.StdNamespace && !StdString)
+ StdString = LookupNamed(S, "string", S.getStdNamespace());
+ if (StdString) {
+ const auto *StdStringTD = llvm::dyn_cast<TypeDecl>(StdString);
+ RetTy = QualType(StdStringTD->getTypeForDecl(), /*Quals=*/0);
}
}
- // All fails then generate a runtime call, this is slow.
-
// Create the wrapper function.
DeclarationName DeclName = &Ctx.Idents.get(CreateUniqName(WrapperName));
QualType FnTy =
@@ -531,48 +360,161 @@ std::string Interpreter::ValueDataToString(const Value &V) {
// FIXME: We still need to understand why we have to get the pointer to the
// underlying Value storage for this to work reliabily...
- if (!V.isManuallyAlloc())
- ValPtr = V.getPtrAddress();
-
- // // Check if the user has implemented a function that avoids fallback to
- // // defaults.
- // if (Ctx.getLangOpts().CPlusPlus) {
- // RuntimeCallName = S.PP.getIdentifierInfo("PrintValueRuntime");
- // } else {
- // PrintingPolicy Policy = Ctx.getPrintingPolicy();
- // Policy.SuppressElaboration = 1;
- // Policy.SuppressTagKeyword = 1;
- // Policy.FullyQualifiedName = 1;
- // std::string TypeStr = QT.getAsString(Policy);
- // printf("KKK %s\n", TypeStr.c_str());
- // // clang::TypeName::getFullyQualifiedName(QT, Ctx, Policy);
- // RuntimeCallName =
- // S.PP.getIdentifierInfo("caas__runtime__PrintValueRuntime__" + TypeStr);
- // }
-
- // LookupResult R(S, RuntimeCallName, SourceLocation(),Sema::LookupOrdinaryName);
- // S.LookupName(R, S.getCurScope());
-
- if (llvm::Error E = BuildWrapperBody(R, S, Ctx, WrapperFD, V.getType(), ValPtr)) {
+ // if (!V.isManuallyAlloc())
+ // ValPtr = V.getPtrAddress();
+
+ if (llvm::Error E =
+ BuildWrapperBody(R, S, Ctx, WrapperFD, V.getType(), ValPtr)) {
llvm::logAllUnhandledErrors(std::move(E), llvm::errs(),
"Fail to build a wrapper function");
- return "{Unable to print the value!}";
+ return "{error: unable to print the value}";
}
auto AddrOrErr = CompileDecl(*this, WrapperFD);
if (!AddrOrErr)
llvm::logAllUnhandledErrors(AddrOrErr.takeError(), llvm::errs(),
"Fail to get symbol address");
- if (auto *Main = AddrOrErr->toPtr<std::string (*)()>())
- return (*Main)();
+ if (StdString) {
+ if (auto *Main = AddrOrErr->toPtr<std::string (*)()>())
+ return (*Main)();
+ } else {
+ if (auto *Main = AddrOrErr->toPtr<const char* (*)()>())
+ return (*Main)();
+ }
return "{Unable to print the value!}";
}
+struct ValueRef: public Value {
+ ValueRef(Interpreter *In, void *Ty) : Value(In, Ty) {
+ // Tell the base class to not try to deallocate if it manages the value.
+ IsManuallyAlloc = false;
+ }
+ void setData(long double D) { Data.m_LongDouble = D; }
+};
+
+std::string Interpreter::ValueDataToString(const Value &V) {
+ Sema &S = getCompilerInstance()->getSema();
+ ASTContext &Ctx = S.getASTContext();
+
+ QualType QT = V.getType();
+
+ while (const ConstantArrayType *CAT = Ctx.getAsConstantArrayType(QT)) {
+ std::string result = "{ ";
+ for (unsigned Idx = 0, N = CAT->getZExtSize(); Idx < N; ++Idx) {
+ QualType ElemTy = CAT->getElementType();
+ ValueRef InnerV = ValueRef(this, ElemTy.getAsOpaquePtr());
+ const Type* BaseTy = CAT->getBaseElementTypeUnsafe();
+ if (ElemTy->isBuiltinType()) {
+ // Arrays model builtin types as a pointer to the builtin. We should
+ // build the dereference to the element type if it is stored as a
+ // builtin.
+ InnerV.setData(V.convertTo<long double>());
+ } else {
+ uintptr_t offset = (uintptr_t)V.getPtr() + Idx * Ctx.getTypeSize(BaseTy) / 8;
+ InnerV.setPtr((void*)offset);
+ }
+ result += ValueDataToString(InnerV);
+ if (Idx < N - 1)
+ result += ", ";
+ }
+ result+=" }";
+ return result;
+ }
+
+ QualType DesugaredTy = QT.getDesugaredType(Ctx);
+ QualType NonRefTy = DesugaredTy.getNonReferenceType();
+
+ LookupResult R = LookupUserDefined(S, QT);
+ if (!R.empty())
+ return CallUserSpecifiedPrinter(R, V);
+
+ // If it is a builtin type dispatch to the builtin overloads.
+ if (auto *BT = DesugaredTy.getCanonicalType()->getAs<BuiltinType>()) {
+ std::string str;
+ llvm::raw_string_ostream ss(str);
+ switch (BT->getKind()) {
+ default:
+ return "{ error: unknown builtin type '" + std::to_string(BT->getKind()) +
+ " '}";
+ case clang::BuiltinType::Bool:
+ ss << ((V.getBool()) ? "true" : "false");
+ return str;
+ case clang::BuiltinType::Char_S:
+ ss << V.getChar_S();
+ return str;
+ case clang::BuiltinType::SChar:
+ ss << V.getSChar();
+ return str;
+ case clang::BuiltinType::Char_U:
+ ss << V.getChar_U();
+ return str;
+ case clang::BuiltinType::UChar:
+ ss << V.getUChar();
+ return str;
+ case clang::BuiltinType::Short:
+ ss << V.getShort();
+ return str;
+ case clang::BuiltinType::UShort:
+ ss << V.getUShort();
+ return str;
+ case clang::BuiltinType::Int:
+ ss << V.getInt();
+ return str;
+ case clang::BuiltinType::UInt:
+ ss << V.getUInt();
+ return str;
+ case clang::BuiltinType::Long:
+ ss << V.getLong();
+ return str;
+ case clang::BuiltinType::ULong:
+ ss << V.getULong();
+ return str;
+ case clang::BuiltinType::LongLong:
+ ss << V.getLongLong();
+ return str;
+ case clang::BuiltinType::ULongLong:
+ ss << V.getULongLong();
+ return str;
+ case clang::BuiltinType::Float:
+ ss << llvm::format("%#.6g", V.getFloat()) << 'f';
+ return str;
+ case clang::BuiltinType::Double:
+ ss << llvm::format("%#.12g", V.getDouble());
+ return str;
+ case clang::BuiltinType::LongDouble:
+ ss << llvm::format("%#.8Lg", V.getLongDouble()) << 'L';
+ return str;
+ }
+ }
+
+ if ((NonRefTy->isPointerType() || NonRefTy->isMemberPointerType()) &&
+ NonRefTy->getPointeeType()->isFunctionProtoType())
+ return FunctionToString(V, V.getPtr());
+
+ if (NonRefTy->isFunctionType())
+ return FunctionToString(V, &V);
+
+ if (NonRefTy->isEnumeralType())
+ return EnumToString(V);
+
+ if (NonRefTy->isNullPtrType())
+ return "nullptr\n";
+
+ // FIXME: Add support for custom printers in C.
+ if (NonRefTy->isPointerType()) {
+ if (NonRefTy->getPointeeType()->isCharType())
+ return AddressToString((const char **)V.getPtrAddress(), '@');
+ }
+
+ // Fall back to printing just the address of the unknown object.
+ return AddressToString(V.getPtr(), '@');
+}
+
std::string Interpreter::ValueTypeToString(const Value &V) const {
ASTContext &Ctx = const_cast<ASTContext &>(V.getASTContext());
QualType QT = V.getType();
- std::string QTStr = PrintQualType(Ctx, QT);
+ std::string QTStr = QualTypeToString(Ctx, QT);
if (QT->isReferenceType())
QTStr += " &";
@@ -622,7 +564,8 @@ class InterfaceKindVisitor
InterfaceKind VisitRecordType(const RecordType *Ty) {
if (S.getLangOpts().CPlusPlus)
return InterfaceKind::WithAlloc;
- ExprResult AddrOfE = S.CreateBuiltinUnaryOp(SourceLocation(), UO_AddrOf, E->IgnoreImpCasts());
+ ExprResult AddrOfE = S.CreateBuiltinUnaryOp(SourceLocation(), UO_AddrOf,
+ E->IgnoreImpCasts());
assert(!AddrOfE.isInvalid() && "Can not create unary expression");
Args.push_back(AddrOfE.get());
return InterfaceKind::NoAlloc;
@@ -697,9 +640,9 @@ class InterfaceKindVisitor
};
static constexpr llvm::StringRef VPName[] = {
- "__clang_Interpreter_SetValueNoAlloc",
- "__clang_Interpreter_SetValueWithAlloc",
- "__clang_Interpreter_SetValueCopyArr", "__ci_newtag"};
+ "__clang_Interpreter_SetValueNoAlloc",
+ "__clang_Interpreter_SetValueWithAlloc",
+ "__clang_Interpreter_SetValueCopyArr", "__ci_newtag"};
// This synthesizes a call expression to a speciall
// function that is responsible for generating the Value.
@@ -798,8 +741,9 @@ llvm::Expected<Expr *> Interpreter::convertExprToValue(Expr *E) {
S.ActOnCallExpr(Scope, ValuePrintingInfo[InterfaceKind::WithAlloc],
E->getBeginLoc(), AdjustedArgs, E->getEndLoc());
if (AllocCall.isInvalid())
- return llvm::make_error<llvm::StringError>("Cannot call to " + VPName[WithAlloc],
- llvm::inconvertibleErrorCode());
+ return llvm::make_error<llvm::StringError>(
+ "Cannot call to " + VPName[WithAlloc],
+ llvm::inconvertibleErrorCode());
TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ty, SourceLocation());
@@ -822,10 +766,11 @@ llvm::Expected<Expr *> Interpreter::convertExprToValue(Expr *E) {
S.ActOnCallExpr(Scope, ValuePrintingInfo[InterfaceKind::CopyArray],
SourceLocation(), Args, SourceLocation());
if (SetValueE.isInvalid())
- return llvm::make_error<llvm::StringError>("Cannot call to " + VPName[CopyArray],
- llvm::inconvertibleErrorCode());
+ return llvm::make_error<llvm::StringError>(
+ "Cannot call to " + VPName[CopyArray],
+ llvm::inconvertibleErrorCode());
break;
- //return SetValueE.get();
+ // return SetValueE.get();
}
Expr *Args[] = {AllocCall.get(), ValuePrintingInfo[InterfaceKind::NewTag]};
ExprResult CXXNewCall = S.BuildCXXNew(
@@ -837,8 +782,8 @@ llvm::Expected<Expr *> Interpreter::convertExprToValue(Expr *E) {
if (CXXNewCall.isInvalid())
return llvm::make_error<llvm::StringError>(
- "Cannot build a call to placement new",
- llvm::inconvertibleErrorCode());
+ "Cannot build a call to placement new",
+ llvm::inconvertibleErrorCode());
SetValueE = S.ActOnFinishFullExpr(CXXNewCall.get(),
/*DiscardedValue=*/false);
diff --git a/clang/test/Interpreter/pretty-print.cpp b/clang/test/Interpreter/pretty-print.cpp
index 87f595fd03360b..6e15a8855db42e 100644
--- a/clang/test/Interpreter/pretty-print.cpp
+++ b/clang/test/Interpreter/pretty-print.cpp
@@ -11,7 +11,7 @@ struct NonPOD { \
NonPOD(): I(sI++) {} \
};
namespace caas { namespace runtime { \
- const char* PrintValueRuntime(const NonPOD* type) { \
+ const char* to_string(const NonPOD* type) { \
switch (type->I) { \
default: return "out-of-bounds"; \
case 0: return "0"; case 1: return "1"; case 2: return "2"; \
@@ -27,6 +27,10 @@ NonPOD non_pod_arr[2][3];
non_pod_arr
// CHECK: (NonPOD[2][3]) { { 0, 1, 2 }, { 3, 4, 5 } }
+char ch_arr[2][3][1] = {{{'a'}, {'b'}, {'c'}}, {{'d'}, {'e'}, {'f'}}};
+ch_arr
+// CHECK: (char[2][3][1]) {{{'a'}, {'b'}, {'c'}}, {{'d'}, {'e'}, {'f'}}}
+
struct S3 { int* p; S3() { p = new int(42); } ~S3() { delete p; } };
S3{}
// CHECK-NEXT: (S3) @0x{{[0-9a-f]+}}
@@ -148,7 +152,7 @@ struct MyDate { \
#include <string>
namespace caas { namespace runtime { \
- std::string PrintValueRuntime(const MyDate* d) { \
+ std::string to_string(const MyDate* d) { \
std::string result; \
result = std::to_string(d->year) + '-'; \
switch(d->month) { default: result += "invalid month"; break; \
diff --git a/clang/tools/clang-repl/ClangRepl.cpp b/clang/tools/clang-repl/ClangRepl.cpp
index f4efb5e1b49eec..fcdfdd2b475bfa 100644
--- a/clang/tools/clang-repl/ClangRepl.cpp
+++ b/clang/tools/clang-repl/ClangRepl.cpp
@@ -231,8 +231,8 @@ int main(int argc, const char **argv) {
llvm::StringRef L = *Line;
L = L.trim();
if (L.ends_with("\\")) {
- // FIXME: Support #ifdef X \ ...
Input += L.drop_back(1);
+ Input += "\n";
LE.setPrompt("clang-repl... ");
continue;
}
More information about the cfe-commits
mailing list