323 lines · cpp
1#include "../../../lib/AST/ByteCode/Context.h"2#include "../../../lib/AST/ByteCode/Descriptor.h"3#include "../../../lib/AST/ByteCode/Program.h"4#include "clang/AST/ASTContext.h"5#include "clang/AST/Decl.h"6#include "clang/ASTMatchers/ASTMatchFinder.h"7#include "clang/ASTMatchers/ASTMatchers.h"8#include "clang/Tooling/Tooling.h"9#include "gtest/gtest.h"10 11using namespace clang;12using namespace clang::interp;13using namespace clang::ast_matchers;14 15/// Test the various toAPValue implementations.16TEST(ToAPValue, Pointers) {17 constexpr char Code[] =18 "struct A { bool a; bool z; };\n"19 "struct S {\n"20 " A a[3];\n"21 "};\n"22 "constexpr S d = {{{true, false}, {false, true}, {false, false}}};\n"23 "constexpr const bool *b = &d.a[1].z;\n"24 "const void *p = (void*)12;\n"25 "const void *nullp = (void*)0;\n"26 "extern int earr[5][5];\n"27 "constexpr const int *arrp = &earr[2][4];\n";28 29 auto AST = tooling::buildASTFromCodeWithArgs(30 Code, {"-fexperimental-new-constant-interpreter"});31 32 auto &ASTCtx = AST->getASTContext();33 auto &Ctx = AST->getASTContext().getInterpContext();34 Program &Prog = Ctx.getProgram();35 36 auto getDecl = [&](const char *Name) -> const ValueDecl * {37 auto Nodes =38 match(valueDecl(hasName(Name)).bind("var"), AST->getASTContext());39 assert(Nodes.size() == 1);40 const auto *D = Nodes[0].getNodeAs<ValueDecl>("var");41 assert(D);42 return D;43 };44 auto getGlobalPtr = [&](const char *Name) -> Pointer {45 const VarDecl *D = cast<VarDecl>(getDecl(Name));46 return Prog.getPtrGlobal(*Prog.getGlobal(D));47 };48 49 {50 const Pointer &GP = getGlobalPtr("b");51 const Pointer &P = GP.deref<Pointer>();52 ASSERT_TRUE(P.isLive());53 APValue A = P.toAPValue(ASTCtx);54 ASSERT_TRUE(A.isLValue());55 ASSERT_TRUE(A.hasLValuePath());56 const auto &Path = A.getLValuePath();57 ASSERT_EQ(Path.size(), 3u);58 ASSERT_EQ(A.getLValueBase(), getDecl("d"));59 // FIXME: Also test all path elements.60 }61 62 {63 const ValueDecl *D = getDecl("p");64 ASSERT_NE(D, nullptr);65 const Pointer &GP = getGlobalPtr("p");66 const Pointer &P = GP.deref<Pointer>();67 ASSERT_TRUE(P.isIntegralPointer());68 APValue A = P.toAPValue(ASTCtx);69 ASSERT_TRUE(A.isLValue());70 ASSERT_TRUE(A.getLValueBase().isNull());71 APSInt I;72 bool Success = A.toIntegralConstant(I, D->getType(), AST->getASTContext());73 ASSERT_TRUE(Success);74 ASSERT_EQ(I, 12);75 }76 77 {78 const ValueDecl *D = getDecl("nullp");79 ASSERT_NE(D, nullptr);80 const Pointer &GP = getGlobalPtr("nullp");81 const Pointer &P = GP.deref<Pointer>();82 ASSERT_TRUE(P.isIntegralPointer());83 APValue A = P.toAPValue(ASTCtx);84 ASSERT_TRUE(A.isLValue());85 ASSERT_TRUE(A.getLValueBase().isNull());86 ASSERT_TRUE(A.isNullPointer());87 APSInt I;88 bool Success = A.toIntegralConstant(I, D->getType(), AST->getASTContext());89 ASSERT_TRUE(Success);90 ASSERT_EQ(I, 0);91 }92 93 // A multidimensional array.94 {95 const ValueDecl *D = getDecl("arrp");96 ASSERT_NE(D, nullptr);97 const Pointer &GP = getGlobalPtr("arrp").deref<Pointer>();98 APValue A = GP.toAPValue(ASTCtx);99 ASSERT_TRUE(A.isLValue());100 ASSERT_TRUE(A.hasLValuePath());101 ASSERT_EQ(A.getLValuePath().size(), 2u);102 ASSERT_EQ(A.getLValuePath()[0].getAsArrayIndex(), 2u);103 ASSERT_EQ(A.getLValuePath()[1].getAsArrayIndex(), 4u);104 ASSERT_EQ(A.getLValueOffset().getQuantity(), 56u);105 ASSERT_TRUE(106 GP.atIndex(0).getFieldDesc()->getElemQualType()->isIntegerType());107 }108}109 110TEST(ToAPValue, FunctionPointers) {111 constexpr char Code[] = " constexpr bool foo() { return true; }\n"112 " constexpr bool (*func)() = foo;\n"113 " constexpr bool (*nullp)() = nullptr;\n";114 115 auto AST = tooling::buildASTFromCodeWithArgs(116 Code, {"-fexperimental-new-constant-interpreter"});117 118 auto &ASTCtx = AST->getASTContext();119 auto &Ctx = AST->getASTContext().getInterpContext();120 Program &Prog = Ctx.getProgram();121 122 auto getDecl = [&](const char *Name) -> const ValueDecl * {123 auto Nodes =124 match(valueDecl(hasName(Name)).bind("var"), AST->getASTContext());125 assert(Nodes.size() == 1);126 const auto *D = Nodes[0].getNodeAs<ValueDecl>("var");127 assert(D);128 return D;129 };130 131 auto getGlobalPtr = [&](const char *Name) -> Pointer {132 const VarDecl *D = cast<VarDecl>(getDecl(Name));133 return Prog.getPtrGlobal(*Prog.getGlobal(D));134 };135 136 {137 const Pointer &GP = getGlobalPtr("func");138 const Pointer &FP = GP.deref<Pointer>();139 ASSERT_FALSE(FP.isZero());140 APValue A = FP.toAPValue(ASTCtx);141 ASSERT_TRUE(A.hasValue());142 ASSERT_TRUE(A.isLValue());143 ASSERT_TRUE(A.hasLValuePath());144 const auto &Path = A.getLValuePath();145 ASSERT_EQ(Path.size(), 0u);146 ASSERT_FALSE(A.getLValueBase().isNull());147 ASSERT_EQ(A.getLValueBase().dyn_cast<const ValueDecl *>(), getDecl("foo"));148 }149 150 {151 const ValueDecl *D = getDecl("nullp");152 ASSERT_NE(D, nullptr);153 const Pointer &GP = getGlobalPtr("nullp");154 const auto &P = GP.deref<FunctionPointer>();155 APValue A = P.toAPValue(ASTCtx);156 ASSERT_TRUE(A.isLValue());157 ASSERT_TRUE(A.getLValueBase().isNull());158 ASSERT_TRUE(A.isNullPointer());159 APSInt I;160 bool Success = A.toIntegralConstant(I, D->getType(), AST->getASTContext());161 ASSERT_TRUE(Success);162 ASSERT_EQ(I, 0);163 }164}165 166TEST(ToAPValue, FunctionPointersC) {167 // NB: The declaration of func2 is useless, but it makes us register a global168 // variable for func.169 constexpr char Code[] = "const int (* const func)(int *) = (void*)17;\n"170 "const int (*func2)(int *) = func;\n";171 auto AST = tooling::buildASTFromCodeWithArgs(172 Code, {"-x", "c", "-fexperimental-new-constant-interpreter"});173 174 auto &ASTCtx = AST->getASTContext();175 auto &Ctx = AST->getASTContext().getInterpContext();176 Program &Prog = Ctx.getProgram();177 178 auto getDecl = [&](const char *Name) -> const ValueDecl * {179 auto Nodes =180 match(valueDecl(hasName(Name)).bind("var"), AST->getASTContext());181 assert(Nodes.size() == 1);182 const auto *D = Nodes[0].getNodeAs<ValueDecl>("var");183 assert(D);184 return D;185 };186 187 auto getGlobalPtr = [&](const char *Name) -> Pointer {188 const VarDecl *D = cast<VarDecl>(getDecl(Name));189 return Prog.getPtrGlobal(*Prog.getGlobal(D));190 };191 192 {193 const ValueDecl *D = getDecl("func");194 const Pointer &GP = getGlobalPtr("func");195 ASSERT_TRUE(GP.isLive());196 const Pointer &FP = GP.deref<Pointer>();197 ASSERT_FALSE(FP.isZero());198 APValue A = FP.toAPValue(ASTCtx);199 ASSERT_TRUE(A.hasValue());200 ASSERT_TRUE(A.isLValue());201 const auto &Path = A.getLValuePath();202 ASSERT_EQ(Path.size(), 0u);203 ASSERT_TRUE(A.getLValueBase().isNull());204 APSInt I;205 bool Success = A.toIntegralConstant(I, D->getType(), AST->getASTContext());206 ASSERT_TRUE(Success);207 ASSERT_EQ(I, 17);208 }209}210 211TEST(ToAPValue, MemberPointers) {212 constexpr char Code[] = "struct S {\n"213 " int m, n;\n"214 "};\n"215 "constexpr int S::*pm = &S::m;\n"216 "constexpr int S::*nn = nullptr;\n";217 218 auto AST = tooling::buildASTFromCodeWithArgs(219 Code, {"-fexperimental-new-constant-interpreter"});220 221 auto &ASTCtx = AST->getASTContext();222 auto &Ctx = AST->getASTContext().getInterpContext();223 Program &Prog = Ctx.getProgram();224 225 auto getDecl = [&](const char *Name) -> const ValueDecl * {226 auto Nodes =227 match(valueDecl(hasName(Name)).bind("var"), AST->getASTContext());228 assert(Nodes.size() == 1);229 const auto *D = Nodes[0].getNodeAs<ValueDecl>("var");230 assert(D);231 return D;232 };233 234 auto getGlobalPtr = [&](const char *Name) -> Pointer {235 const VarDecl *D = cast<VarDecl>(getDecl(Name));236 return Prog.getPtrGlobal(*Prog.getGlobal(D));237 };238 239 {240 const Pointer &GP = getGlobalPtr("pm");241 ASSERT_TRUE(GP.isLive());242 const MemberPointer &FP = GP.deref<MemberPointer>();243 APValue A = FP.toAPValue(ASTCtx);244 ASSERT_EQ(A.getMemberPointerDecl(), getDecl("m"));245 ASSERT_EQ(A.getKind(), APValue::MemberPointer);246 }247 248 {249 const Pointer &GP = getGlobalPtr("nn");250 ASSERT_TRUE(GP.isLive());251 const MemberPointer &NP = GP.deref<MemberPointer>();252 ASSERT_TRUE(NP.isZero());253 APValue A = NP.toAPValue(ASTCtx);254 ASSERT_EQ(A.getKind(), APValue::MemberPointer);255 }256}257 258/// Compare outputs between the two interpreters.259TEST(ToAPValue, Comparison) {260 constexpr char Code[] =261 "constexpr int GI = 12;\n"262 "constexpr const int *PI = &GI;\n"263 "struct S{int a; };\n"264 "constexpr S GS[] = {{}, {}};\n"265 "constexpr const S* OS = GS + 1;\n"266 "constexpr S DS = *OS;\n"267 "constexpr int IA[2][2][2] = {{{1, 2}, {3, 4}}, {{5, 6}, {7, 8}}};\n"268 "constexpr const int *PIA = IA[1][1];\n";269 270 for (const char *Arg : {"", "-fexperimental-new-constant-interpreter"}) {271 auto AST = tooling::buildASTFromCodeWithArgs(Code, {Arg});272 273 auto getDecl = [&](const char *Name) -> const VarDecl * {274 auto Nodes =275 match(valueDecl(hasName(Name)).bind("var"), AST->getASTContext());276 assert(Nodes.size() == 1);277 const auto *D = Nodes[0].getNodeAs<ValueDecl>("var");278 assert(D);279 return cast<VarDecl>(D);280 };281 282 {283 const VarDecl *GIDecl = getDecl("GI");284 const APValue *V = GIDecl->evaluateValue();285 ASSERT_TRUE(V->isInt());286 }287 288 {289 const VarDecl *GIDecl = getDecl("GI");290 const VarDecl *PIDecl = getDecl("PI");291 const APValue *V = PIDecl->evaluateValue();292 ASSERT_TRUE(V->isLValue());293 ASSERT_TRUE(V->hasLValuePath());294 ASSERT_EQ(V->getLValueBase().get<const ValueDecl *>(), GIDecl);295 ASSERT_EQ(V->getLValuePath().size(), 0u);296 }297 298 {299 const APValue *V = getDecl("OS")->evaluateValue();300 ASSERT_TRUE(V->isLValue());301 ASSERT_EQ(V->getLValueOffset().getQuantity(), (unsigned)sizeof(int));302 ASSERT_TRUE(V->hasLValuePath());303 ASSERT_EQ(V->getLValuePath().size(), 1u);304 ASSERT_EQ(V->getLValuePath()[0].getAsArrayIndex(), 1u);305 }306 307 {308 const APValue *V = getDecl("DS")->evaluateValue();309 ASSERT_TRUE(V->isStruct());310 }311 {312 const APValue *V = getDecl("PIA")->evaluateValue();313 ASSERT_TRUE(V->isLValue());314 ASSERT_TRUE(V->hasLValuePath());315 ASSERT_EQ(V->getLValuePath().size(), 3u);316 ASSERT_EQ(V->getLValuePath()[0].getAsArrayIndex(), 1u);317 ASSERT_EQ(V->getLValuePath()[1].getAsArrayIndex(), 1u);318 ASSERT_EQ(V->getLValuePath()[2].getAsArrayIndex(), 0u);319 ASSERT_EQ(V->getLValueOffset().getQuantity(), (unsigned)sizeof(int) * 6);320 }321 }322}323