diff options
author | Jun Zhang <jun@junz.org> | 2023-05-16 20:10:49 +0800 |
---|---|---|
committer | Jun Zhang <jun@junz.org> | 2023-05-16 20:10:49 +0800 |
commit | a423b7f1d7ca8b263af85944f57a69aa08fc942c (patch) | |
tree | 08a01eea0d617018a9137e15e3a9021a7fe6ccf4 /clang/unittests | |
parent | 247fa04116a6cabf8378c6c72d90b2f705e969de (diff) | |
download | llvm-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.txt | 2 | ||||
-rw-r--r-- | clang/unittests/Interpreter/InterpreterTest.cpp | 100 |
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 |