summaryrefslogtreecommitdiff
path: root/clang/unittests
diff options
context:
space:
mode:
authorJun Zhang <jun@junz.org>2023-05-16 20:10:49 +0800
committerJun Zhang <jun@junz.org>2023-05-16 20:10:49 +0800
commita423b7f1d7ca8b263af85944f57a69aa08fc942c (patch)
tree08a01eea0d617018a9137e15e3a9021a7fe6ccf4 /clang/unittests
parent247fa04116a6cabf8378c6c72d90b2f705e969de (diff)
downloadllvm-a423b7f1d7ca8b263af85944f57a69aa08fc942c.tar.gz
[clang-repl] Introduce Value to capture expression results
This is the second part of the below RFC: https://discourse.llvm.org/t/rfc-handle-execution-results-in-clang-repl/68493 This patch implements a Value class that can be used to carry expression results in clang-repl. In other words, when we see a top expression without semi, it will be captured and stored to a Value object. You can explicitly specify where you want to store the object, like: ``` Value V; llvm::cantFail(Interp->ParseAndExecute("int x = 42;")); llvm::cantFail(Interp->ParseAndExecute("x", &V)); ``` `V` now stores some useful infomation about `x`, you can get its real value (42), it's `clang::QualType` or anything interesting. However, if you don't specify the optional argument, it will be captured to a local variable, and automatically called `Value::dump`, which is not implemented yet in this patch. Signed-off-by: Jun Zhang <jun@junz.org>
Diffstat (limited to 'clang/unittests')
-rw-r--r--clang/unittests/Interpreter/CMakeLists.txt2
-rw-r--r--clang/unittests/Interpreter/InterpreterTest.cpp100
2 files changed, 100 insertions, 2 deletions
diff --git a/clang/unittests/Interpreter/CMakeLists.txt b/clang/unittests/Interpreter/CMakeLists.txt
index 1a099dbbfe59..698494b9897f 100644
--- a/clang/unittests/Interpreter/CMakeLists.txt
+++ b/clang/unittests/Interpreter/CMakeLists.txt
@@ -22,3 +22,5 @@ target_link_libraries(ClangReplInterpreterTests PUBLIC
if(NOT WIN32)
add_subdirectory(ExceptionTests)
endif()
+
+export_executable_symbols(ClangReplInterpreterTests)
diff --git a/clang/unittests/Interpreter/InterpreterTest.cpp b/clang/unittests/Interpreter/InterpreterTest.cpp
index d555911a8945..1662de406e40 100644
--- a/clang/unittests/Interpreter/InterpreterTest.cpp
+++ b/clang/unittests/Interpreter/InterpreterTest.cpp
@@ -17,6 +17,7 @@
#include "clang/AST/Mangle.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Interpreter/Value.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Sema.h"
@@ -33,6 +34,11 @@ using namespace clang;
#define CLANG_INTERPRETER_NO_SUPPORT_EXEC
#endif
+int Global = 42;
+// JIT reports symbol not found on Windows without the visibility attribute.
+REPL_EXTERNAL_VISIBILITY int getGlobal() { return Global; }
+REPL_EXTERNAL_VISIBILITY void setGlobal(int val) { Global = val; }
+
namespace {
using Args = std::vector<const char *>;
static std::unique_ptr<Interpreter>
@@ -276,8 +282,7 @@ TEST(IncrementalProcessing, InstantiateTemplate) {
std::vector<const char *> Args = {"-fno-delayed-template-parsing"};
std::unique_ptr<Interpreter> Interp = createInterpreter(Args);
- llvm::cantFail(Interp->Parse("void* operator new(__SIZE_TYPE__, void* __p);"
- "extern \"C\" int printf(const char*,...);"
+ llvm::cantFail(Interp->Parse("extern \"C\" int printf(const char*,...);"
"class A {};"
"struct B {"
" template<typename T>"
@@ -315,4 +320,95 @@ TEST(IncrementalProcessing, InstantiateTemplate) {
free(NewA);
}
+TEST(InterpreterTest, Value) {
+ std::unique_ptr<Interpreter> Interp = createInterpreter();
+
+ Value V1;
+ llvm::cantFail(Interp->ParseAndExecute("int x = 42;"));
+ llvm::cantFail(Interp->ParseAndExecute("x", &V1));
+ EXPECT_TRUE(V1.isValid());
+ EXPECT_TRUE(V1.hasValue());
+ EXPECT_EQ(V1.getInt(), 42);
+ EXPECT_EQ(V1.convertTo<int>(), 42);
+ EXPECT_TRUE(V1.getType()->isIntegerType());
+ EXPECT_EQ(V1.getKind(), Value::K_Int);
+ EXPECT_FALSE(V1.isManuallyAlloc());
+
+ Value V2;
+ llvm::cantFail(Interp->ParseAndExecute("double y = 3.14;"));
+ llvm::cantFail(Interp->ParseAndExecute("y", &V2));
+ EXPECT_TRUE(V2.isValid());
+ EXPECT_TRUE(V2.hasValue());
+ EXPECT_EQ(V2.getDouble(), 3.14);
+ EXPECT_EQ(V2.convertTo<double>(), 3.14);
+ EXPECT_TRUE(V2.getType()->isFloatingType());
+ EXPECT_EQ(V2.getKind(), Value::K_Double);
+ EXPECT_FALSE(V2.isManuallyAlloc());
+
+ Value V3;
+ llvm::cantFail(Interp->ParseAndExecute(
+ "struct S { int* p; S() { p = new int(42); } ~S() { delete p; }};"));
+ llvm::cantFail(Interp->ParseAndExecute("S{}", &V3));
+ EXPECT_TRUE(V3.isValid());
+ EXPECT_TRUE(V3.hasValue());
+ EXPECT_TRUE(V3.getType()->isRecordType());
+ EXPECT_EQ(V3.getKind(), Value::K_PtrOrObj);
+ EXPECT_TRUE(V3.isManuallyAlloc());
+
+ Value V4;
+ llvm::cantFail(Interp->ParseAndExecute("int getGlobal();"));
+ llvm::cantFail(Interp->ParseAndExecute("void setGlobal(int);"));
+ llvm::cantFail(Interp->ParseAndExecute("getGlobal()", &V4));
+ EXPECT_EQ(V4.getInt(), 42);
+ EXPECT_TRUE(V4.getType()->isIntegerType());
+
+ Value V5;
+ // Change the global from the compiled code.
+ setGlobal(43);
+ llvm::cantFail(Interp->ParseAndExecute("getGlobal()", &V5));
+ EXPECT_EQ(V5.getInt(), 43);
+ EXPECT_TRUE(V5.getType()->isIntegerType());
+
+ // Change the global from the interpreted code.
+ llvm::cantFail(Interp->ParseAndExecute("setGlobal(44);"));
+ EXPECT_EQ(getGlobal(), 44);
+
+ Value V6;
+ llvm::cantFail(Interp->ParseAndExecute("void foo() {}"));
+ llvm::cantFail(Interp->ParseAndExecute("foo()", &V6));
+ EXPECT_TRUE(V6.isValid());
+ EXPECT_FALSE(V6.hasValue());
+ EXPECT_TRUE(V6.getType()->isVoidType());
+ EXPECT_EQ(V6.getKind(), Value::K_Void);
+ EXPECT_FALSE(V2.isManuallyAlloc());
+
+ Value V7;
+ llvm::cantFail(Interp->ParseAndExecute("foo", &V7));
+ EXPECT_TRUE(V7.isValid());
+ EXPECT_TRUE(V7.hasValue());
+ EXPECT_TRUE(V7.getType()->isFunctionProtoType());
+ EXPECT_EQ(V7.getKind(), Value::K_PtrOrObj);
+ EXPECT_FALSE(V7.isManuallyAlloc());
+
+ Value V8;
+ llvm::cantFail(Interp->ParseAndExecute("struct SS{ void f() {} };"));
+ llvm::cantFail(Interp->ParseAndExecute("&SS::f", &V8));
+ EXPECT_TRUE(V8.isValid());
+ EXPECT_TRUE(V8.hasValue());
+ EXPECT_TRUE(V8.getType()->isMemberFunctionPointerType());
+ EXPECT_EQ(V8.getKind(), Value::K_PtrOrObj);
+ EXPECT_TRUE(V8.isManuallyAlloc());
+
+ Value V9;
+ llvm::cantFail(Interp->ParseAndExecute("struct A { virtual int f(); };"));
+ llvm::cantFail(
+ Interp->ParseAndExecute("struct B : A { int f() { return 42; }};"));
+ llvm::cantFail(Interp->ParseAndExecute("int (B::*ptr)() = &B::f;"));
+ llvm::cantFail(Interp->ParseAndExecute("ptr", &V9));
+ EXPECT_TRUE(V9.isValid());
+ EXPECT_TRUE(V9.hasValue());
+ EXPECT_TRUE(V9.getType()->isMemberFunctionPointerType());
+ EXPECT_EQ(V9.getKind(), Value::K_PtrOrObj);
+ EXPECT_TRUE(V9.isManuallyAlloc());
+}
} // end anonymous namespace