blob: ab567609f78e3114365d3a74efc2b9d2274a95d9 [file] [log] [blame]
#include "../../../lib/AST/ByteCode/Context.h"
#include "../../../lib/AST/ByteCode/Descriptor.h"
#include "../../../lib/AST/ByteCode/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"
"const void *p = (void*)12;\n"
"const void *nullp = (void*)0;\n"
"extern int earr[5][5];\n"
"constexpr const int *arrp = &earr[2][4];\n";
auto AST = tooling::buildASTFromCodeWithArgs(
Code, {"-fexperimental-new-constant-interpreter"});
auto &ASTCtx = AST->getASTContext();
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<ValueDecl>("var");
assert(D);
return D;
};
auto getGlobalPtr = [&](const char *Name) -> Pointer {
const VarDecl *D = cast<VarDecl>(getDecl(Name));
return Prog.getPtrGlobal(*Prog.getGlobal(D));
};
{
const Pointer &GP = getGlobalPtr("b");
const Pointer &P = GP.deref<Pointer>();
ASSERT_TRUE(P.isLive());
APValue A = P.toAPValue(ASTCtx);
ASSERT_TRUE(A.isLValue());
ASSERT_TRUE(A.hasLValuePath());
const auto &Path = A.getLValuePath();
ASSERT_EQ(Path.size(), 3u);
ASSERT_EQ(A.getLValueBase(), getDecl("d"));
// FIXME: Also test all path elements.
}
{
const ValueDecl *D = getDecl("p");
ASSERT_NE(D, nullptr);
const Pointer &GP = getGlobalPtr("p");
const Pointer &P = GP.deref<Pointer>();
ASSERT_TRUE(P.isIntegralPointer());
APValue A = P.toAPValue(ASTCtx);
ASSERT_TRUE(A.isLValue());
ASSERT_TRUE(A.getLValueBase().isNull());
APSInt I;
bool Success = A.toIntegralConstant(I, D->getType(), AST->getASTContext());
ASSERT_TRUE(Success);
ASSERT_EQ(I, 12);
}
{
const ValueDecl *D = getDecl("nullp");
ASSERT_NE(D, nullptr);
const Pointer &GP = getGlobalPtr("nullp");
const Pointer &P = GP.deref<Pointer>();
ASSERT_TRUE(P.isIntegralPointer());
APValue A = P.toAPValue(ASTCtx);
ASSERT_TRUE(A.isLValue());
ASSERT_TRUE(A.getLValueBase().isNull());
ASSERT_TRUE(A.isNullPointer());
APSInt I;
bool Success = A.toIntegralConstant(I, D->getType(), AST->getASTContext());
ASSERT_TRUE(Success);
ASSERT_EQ(I, 0);
}
// A multidimensional array.
{
const ValueDecl *D = getDecl("arrp");
ASSERT_NE(D, nullptr);
const Pointer &GP = getGlobalPtr("arrp").deref<Pointer>();
APValue A = GP.toAPValue(ASTCtx);
ASSERT_TRUE(A.isLValue());
ASSERT_TRUE(A.hasLValuePath());
ASSERT_EQ(A.getLValuePath().size(), 2u);
ASSERT_EQ(A.getLValuePath()[0].getAsArrayIndex(), 2u);
ASSERT_EQ(A.getLValuePath()[1].getAsArrayIndex(), 4u);
ASSERT_EQ(A.getLValueOffset().getQuantity(), 56u);
ASSERT_TRUE(
GP.atIndex(0).getFieldDesc()->getElemQualType()->isIntegerType());
}
}
TEST(ToAPValue, FunctionPointers) {
constexpr char Code[] = " constexpr bool foo() { return true; }\n"
" constexpr bool (*func)() = foo;\n"
" constexpr bool (*nullp)() = nullptr;\n";
auto AST = tooling::buildASTFromCodeWithArgs(
Code, {"-fexperimental-new-constant-interpreter"});
auto &ASTCtx = AST->getASTContext();
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<ValueDecl>("var");
assert(D);
return D;
};
auto getGlobalPtr = [&](const char *Name) -> Pointer {
const VarDecl *D = cast<VarDecl>(getDecl(Name));
return Prog.getPtrGlobal(*Prog.getGlobal(D));
};
{
const Pointer &GP = getGlobalPtr("func");
const FunctionPointer &FP = GP.deref<FunctionPointer>();
ASSERT_FALSE(FP.isZero());
APValue A = FP.toAPValue(ASTCtx);
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<const ValueDecl *>(), getDecl("foo"));
}
{
const ValueDecl *D = getDecl("nullp");
ASSERT_NE(D, nullptr);
const Pointer &GP = getGlobalPtr("nullp");
const auto &P = GP.deref<FunctionPointer>();
APValue A = P.toAPValue(ASTCtx);
ASSERT_TRUE(A.isLValue());
ASSERT_TRUE(A.getLValueBase().isNull());
ASSERT_TRUE(A.isNullPointer());
APSInt I;
bool Success = A.toIntegralConstant(I, D->getType(), AST->getASTContext());
ASSERT_TRUE(Success);
ASSERT_EQ(I, 0);
}
}
TEST(ToAPValue, FunctionPointersC) {
// NB: The declaration of func2 is useless, but it makes us register a global
// variable for func.
constexpr char Code[] = "const int (* const func)(int *) = (void*)17;\n"
"const int (*func2)(int *) = func;\n";
auto AST = tooling::buildASTFromCodeWithArgs(
Code, {"-x", "c", "-fexperimental-new-constant-interpreter"});
auto &ASTCtx = AST->getASTContext();
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<ValueDecl>("var");
assert(D);
return D;
};
auto getGlobalPtr = [&](const char *Name) -> Pointer {
const VarDecl *D = cast<VarDecl>(getDecl(Name));
return Prog.getPtrGlobal(*Prog.getGlobal(D));
};
{
const ValueDecl *D = getDecl("func");
const Pointer &GP = getGlobalPtr("func");
ASSERT_TRUE(GP.isLive());
const FunctionPointer &FP = GP.deref<FunctionPointer>();
ASSERT_FALSE(FP.isZero());
APValue A = FP.toAPValue(ASTCtx);
ASSERT_TRUE(A.hasValue());
ASSERT_TRUE(A.isLValue());
const auto &Path = A.getLValuePath();
ASSERT_EQ(Path.size(), 0u);
ASSERT_TRUE(A.getLValueBase().isNull());
APSInt I;
bool Success = A.toIntegralConstant(I, D->getType(), AST->getASTContext());
ASSERT_TRUE(Success);
ASSERT_EQ(I, 17);
}
}
TEST(ToAPValue, MemberPointers) {
constexpr char Code[] = "struct S {\n"
" int m, n;\n"
"};\n"
"constexpr int S::*pm = &S::m;\n"
"constexpr int S::*nn = nullptr;\n";
auto AST = tooling::buildASTFromCodeWithArgs(
Code, {"-fexperimental-new-constant-interpreter"});
auto &ASTCtx = AST->getASTContext();
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<ValueDecl>("var");
assert(D);
return D;
};
auto getGlobalPtr = [&](const char *Name) -> Pointer {
const VarDecl *D = cast<VarDecl>(getDecl(Name));
return Prog.getPtrGlobal(*Prog.getGlobal(D));
};
{
const Pointer &GP = getGlobalPtr("pm");
ASSERT_TRUE(GP.isLive());
const MemberPointer &FP = GP.deref<MemberPointer>();
APValue A = FP.toAPValue(ASTCtx);
ASSERT_EQ(A.getMemberPointerDecl(), getDecl("m"));
ASSERT_EQ(A.getKind(), APValue::MemberPointer);
}
{
const Pointer &GP = getGlobalPtr("nn");
ASSERT_TRUE(GP.isLive());
const MemberPointer &NP = GP.deref<MemberPointer>();
ASSERT_TRUE(NP.isZero());
APValue A = NP.toAPValue(ASTCtx);
ASSERT_EQ(A.getKind(), APValue::MemberPointer);
}
}
/// Compare outputs between the two interpreters.
TEST(ToAPValue, Comparison) {
constexpr char Code[] =
"constexpr int GI = 12;\n"
"constexpr const int *PI = &GI;\n"
"struct S{int a; };\n"
"constexpr S GS[] = {{}, {}};\n"
"constexpr const S* OS = GS + 1;\n"
"constexpr S DS = *OS;\n"
"constexpr int IA[2][2][2] = {{{1, 2}, {3, 4}}, {{5, 6}, {7, 8}}};\n"
"constexpr const int *PIA = IA[1][1];\n";
for (const char *Arg : {"", "-fexperimental-new-constant-interpreter"}) {
auto AST = tooling::buildASTFromCodeWithArgs(Code, {Arg});
auto getDecl = [&](const char *Name) -> const VarDecl * {
auto Nodes =
match(valueDecl(hasName(Name)).bind("var"), AST->getASTContext());
assert(Nodes.size() == 1);
const auto *D = Nodes[0].getNodeAs<ValueDecl>("var");
assert(D);
return cast<VarDecl>(D);
};
{
const VarDecl *GIDecl = getDecl("GI");
const APValue *V = GIDecl->evaluateValue();
ASSERT_TRUE(V->isInt());
}
{
const VarDecl *GIDecl = getDecl("GI");
const VarDecl *PIDecl = getDecl("PI");
const APValue *V = PIDecl->evaluateValue();
ASSERT_TRUE(V->isLValue());
ASSERT_TRUE(V->hasLValuePath());
ASSERT_EQ(V->getLValueBase().get<const ValueDecl *>(), GIDecl);
ASSERT_EQ(V->getLValuePath().size(), 0u);
}
{
const APValue *V = getDecl("OS")->evaluateValue();
ASSERT_TRUE(V->isLValue());
ASSERT_EQ(V->getLValueOffset().getQuantity(), (unsigned)sizeof(int));
ASSERT_TRUE(V->hasLValuePath());
ASSERT_EQ(V->getLValuePath().size(), 1u);
ASSERT_EQ(V->getLValuePath()[0].getAsArrayIndex(), 1u);
}
{
const APValue *V = getDecl("DS")->evaluateValue();
ASSERT_TRUE(V->isStruct());
}
{
const APValue *V = getDecl("PIA")->evaluateValue();
ASSERT_TRUE(V->isLValue());
ASSERT_TRUE(V->hasLValuePath());
ASSERT_EQ(V->getLValuePath().size(), 3u);
ASSERT_EQ(V->getLValuePath()[0].getAsArrayIndex(), 1u);
ASSERT_EQ(V->getLValuePath()[1].getAsArrayIndex(), 1u);
ASSERT_EQ(V->getLValuePath()[2].getAsArrayIndex(), 0u);
ASSERT_EQ(V->getLValueOffset().getQuantity(), (unsigned)sizeof(int) * 6);
}
}
}