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, Args: {"-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(Matcher: valueDecl(hasName(Name)).bind(ID: "var"), Context&: AST->getASTContext());
39 assert(Nodes.size() == 1);
40 const auto *D = Nodes[0].getNodeAs<ValueDecl>(ID: "var");
41 assert(D);
42 return D;
43 };
44 auto getGlobalPtr = [&](const char *Name) -> Pointer {
45 const VarDecl *D = cast<VarDecl>(Val: getDecl(Name));
46 return Prog.getPtrGlobal(Idx: *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(Result&: I, SrcTy: D->getType(), Ctx: 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(Result&: I, SrcTy: D->getType(), Ctx: 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, Args: {"-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(Matcher: valueDecl(hasName(Name)).bind(ID: "var"), Context&: AST->getASTContext());
125 assert(Nodes.size() == 1);
126 const auto *D = Nodes[0].getNodeAs<ValueDecl>(ID: "var");
127 assert(D);
128 return D;
129 };
130
131 auto getGlobalPtr = [&](const char *Name) -> Pointer {
132 const VarDecl *D = cast<VarDecl>(Val: getDecl(Name));
133 return Prog.getPtrGlobal(Idx: *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(Result&: I, SrcTy: D->getType(), Ctx: 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 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, Args: {"-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(Matcher: valueDecl(hasName(Name)).bind(ID: "var"), Context&: AST->getASTContext());
181 assert(Nodes.size() == 1);
182 const auto *D = Nodes[0].getNodeAs<ValueDecl>(ID: "var");
183 assert(D);
184 return D;
185 };
186
187 auto getGlobalPtr = [&](const char *Name) -> Pointer {
188 const VarDecl *D = cast<VarDecl>(Val: getDecl(Name));
189 return Prog.getPtrGlobal(Idx: *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(Result&: I, SrcTy: D->getType(), Ctx: 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, Args: {"-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(Matcher: valueDecl(hasName(Name)).bind(ID: "var"), Context&: AST->getASTContext());
228 assert(Nodes.size() == 1);
229 const auto *D = Nodes[0].getNodeAs<ValueDecl>(ID: "var");
230 assert(D);
231 return D;
232 };
233
234 auto getGlobalPtr = [&](const char *Name) -> Pointer {
235 const VarDecl *D = cast<VarDecl>(Val: getDecl(Name));
236 return Prog.getPtrGlobal(Idx: *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, Args: {Arg});
272
273 auto getDecl = [&](const char *Name) -> const VarDecl * {
274 auto Nodes =
275 match(Matcher: valueDecl(hasName(Name)).bind(ID: "var"), Context&: AST->getASTContext());
276 assert(Nodes.size() == 1);
277 const auto *D = Nodes[0].getNodeAs<ValueDecl>(ID: "var");
278 assert(D);
279 return cast<VarDecl>(Val: 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

source code of clang/unittests/AST/ByteCode/toAPValue.cpp