diff --git a/clang/unittests/AST/Interp/CMakeLists.txt b/clang/unittests/AST/Interp/CMakeLists.txt index 8fa5c85064dbc..ea727cdd4412b 100644 --- a/clang/unittests/AST/Interp/CMakeLists.txt +++ b/clang/unittests/AST/Interp/CMakeLists.txt @@ -1,5 +1,6 @@ add_clang_unittest(InterpTests Descriptor.cpp + toAPValue.cpp ) clang_target_link_libraries(InterpTests diff --git a/clang/unittests/AST/Interp/toAPValue.cpp b/clang/unittests/AST/Interp/toAPValue.cpp new file mode 100644 index 0000000000000..d0dfb40d51495 --- /dev/null +++ b/clang/unittests/AST/Interp/toAPValue.cpp @@ -0,0 +1,90 @@ +#include "../../../lib/AST/Interp/Context.h" +#include "../../../lib/AST/Interp/Descriptor.h" +#include "../../../lib/AST/Interp/Program.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Tooling/Tooling.h" +#include "gtest/gtest.h" + +using namespace clang; +using namespace clang::interp; +using namespace clang::ast_matchers; + +/// Test the various toAPValue implementations. +TEST(ToAPValue, Pointers) { + constexpr char Code[] = + "struct A { bool a; bool z; };\n" + "struct S {\n" + " A a[3];\n" + "};\n" + "constexpr S d = {{{true, false}, {false, true}, {false, false}}};\n" + "constexpr const bool *b = &d.a[1].z;\n"; + + auto AST = tooling::buildASTFromCodeWithArgs( + Code, {"-fexperimental-new-constant-interpreter"}); + + auto &Ctx = AST->getASTContext().getInterpContext(); + Program &Prog = Ctx.getProgram(); + + auto getDecl = [&](const char *Name) -> const ValueDecl * { + auto Nodes = + match(valueDecl(hasName(Name)).bind("var"), AST->getASTContext()); + assert(Nodes.size() == 1); + const auto *D = Nodes[0].getNodeAs("var"); + assert(D); + return D; + }; + auto getGlobalPtr = [&](const char *Name) -> Pointer { + const VarDecl *D = cast(getDecl(Name)); + return Prog.getPtrGlobal(*Prog.getGlobal(D)); + }; + + const Pointer &GP = getGlobalPtr("b"); + const Pointer &P = GP.deref(); + ASSERT_TRUE(P.isLive()); + APValue A = P.toAPValue(); + ASSERT_TRUE(A.isLValue()); + ASSERT_TRUE(A.hasLValuePath()); + const auto &Path = A.getLValuePath(); + ASSERT_EQ(Path.size(), 3u); + ASSERT_EQ(A.getLValueBase(), getDecl("d")); +} + +TEST(ToAPValue, FunctionPointers) { + constexpr char Code[] = " constexpr bool foo() { return true; }\n" + " constexpr bool (*func)() = foo;\n"; + + auto AST = tooling::buildASTFromCodeWithArgs( + Code, {"-fexperimental-new-constant-interpreter"}); + + auto &Ctx = AST->getASTContext().getInterpContext(); + Program &Prog = Ctx.getProgram(); + + auto getDecl = [&](const char *Name) -> const ValueDecl * { + auto Nodes = + match(valueDecl(hasName(Name)).bind("var"), AST->getASTContext()); + assert(Nodes.size() == 1); + const auto *D = Nodes[0].getNodeAs("var"); + assert(D); + return D; + }; + + auto getGlobalPtr = [&](const char *Name) -> Pointer { + const VarDecl *D = cast(getDecl(Name)); + return Prog.getPtrGlobal(*Prog.getGlobal(D)); + }; + + const Pointer &GP = getGlobalPtr("func"); + const FunctionPointer &FP = GP.deref(); + ASSERT_FALSE(FP.isZero()); + APValue A = FP.toAPValue(); + ASSERT_TRUE(A.hasValue()); + ASSERT_TRUE(A.isLValue()); + ASSERT_TRUE(A.hasLValuePath()); + const auto &Path = A.getLValuePath(); + ASSERT_EQ(Path.size(), 0u); + ASSERT_FALSE(A.getLValueBase().isNull()); + ASSERT_EQ(A.getLValueBase().dyn_cast(), getDecl("foo")); +}