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 11 using namespace clang; 12 using namespace clang::interp; 13 using namespace clang::ast_matchers; 14 15 /// Test the various toAPValue implementations. 16 TEST(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 110 TEST(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 FunctionPointer &FP = GP.deref<FunctionPointer>(); 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 166 TEST(ToAPValue, FunctionPointersC) { 167 // NB: The declaration of func2 is useless, but it makes us register a global 168 // 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 FunctionPointer &FP = GP.deref<FunctionPointer>(); 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 211 TEST(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