1 | #include "../../../lib/AST/ByteCode/Descriptor.h" |
2 | #include "../../../lib/AST/ByteCode/Context.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 | /// Inspect generated Descriptors as well as the pointers we create. |
16 | /// |
17 | TEST(Descriptor, Primitives) { |
18 | constexpr char Code[] = "struct A { bool a; bool b; };\n" |
19 | "struct S {\n" |
20 | " float f;\n" |
21 | " char s[4];\n" |
22 | " A a[3];\n" |
23 | " short l[3][3];\n" |
24 | " int EmptyA[0];\n" |
25 | "};\n" |
26 | "constexpr S d = {0.0, \"foo\", {{true, false}, " |
27 | "{false, true}, {false, false}},\n" |
28 | " {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}, {}};\n" ; |
29 | |
30 | auto AST = tooling::buildASTFromCodeWithArgs( |
31 | Code, Args: {"-fexperimental-new-constant-interpreter" }); |
32 | |
33 | const VarDecl *D = selectFirst<VarDecl>( |
34 | BoundTo: "d" , Results: match(Matcher: varDecl().bind(ID: "d" ), Context&: AST->getASTContext())); |
35 | ASSERT_NE(D, nullptr); |
36 | |
37 | const auto &Ctx = AST->getASTContext().getInterpContext(); |
38 | Program &Prog = Ctx.getProgram(); |
39 | // Global is registered. |
40 | ASSERT_TRUE(Prog.getGlobal(D)); |
41 | |
42 | // Get a Pointer to the global. |
43 | const Pointer &GlobalPtr = Prog.getPtrGlobal(Idx: *Prog.getGlobal(D)); |
44 | |
45 | // Test Descriptor of the struct S. |
46 | const Descriptor *GlobalDesc = GlobalPtr.getFieldDesc(); |
47 | ASSERT_TRUE(GlobalDesc == GlobalPtr.getDeclDesc()); |
48 | |
49 | ASSERT_TRUE(GlobalDesc->asDecl() == D); |
50 | ASSERT_FALSE(GlobalDesc->asExpr()); |
51 | ASSERT_TRUE(GlobalDesc->asValueDecl() == D); |
52 | ASSERT_FALSE(GlobalDesc->asFieldDecl()); |
53 | ASSERT_FALSE(GlobalDesc->asRecordDecl()); |
54 | |
55 | // Still true because this is a global variable. |
56 | ASSERT_TRUE(GlobalDesc->getMetadataSize() == sizeof(GlobalInlineDescriptor)); |
57 | ASSERT_FALSE(GlobalDesc->isPrimitiveArray()); |
58 | ASSERT_FALSE(GlobalDesc->isCompositeArray()); |
59 | ASSERT_FALSE(GlobalDesc->isZeroSizeArray()); |
60 | ASSERT_FALSE(GlobalDesc->isUnknownSizeArray()); |
61 | ASSERT_FALSE(GlobalDesc->isPrimitive()); |
62 | ASSERT_FALSE(GlobalDesc->isArray()); |
63 | ASSERT_TRUE(GlobalDesc->isRecord()); |
64 | |
65 | // Test the Record for the struct S. |
66 | const Record *SRecord = GlobalDesc->ElemRecord; |
67 | ASSERT_TRUE(SRecord); |
68 | ASSERT_TRUE(SRecord->getNumFields() == 5); |
69 | ASSERT_TRUE(SRecord->getNumBases() == 0); |
70 | ASSERT_FALSE(SRecord->getDestructor()); |
71 | |
72 | // First field. |
73 | const Record::Field *F1 = SRecord->getField(I: 0u); |
74 | ASSERT_TRUE(F1); |
75 | ASSERT_FALSE(F1->isBitField()); |
76 | ASSERT_TRUE(F1->Desc->isPrimitive()); |
77 | |
78 | // Second field. |
79 | const Record::Field *F2 = SRecord->getField(I: 1u); |
80 | ASSERT_TRUE(F2); |
81 | ASSERT_FALSE(F2->isBitField()); |
82 | ASSERT_TRUE(F2->Desc->isArray()); |
83 | ASSERT_FALSE(F2->Desc->isCompositeArray()); |
84 | ASSERT_TRUE(F2->Desc->isPrimitiveArray()); |
85 | ASSERT_FALSE(F2->Desc->isPrimitive()); |
86 | ASSERT_FALSE(F2->Desc->ElemDesc); |
87 | ASSERT_EQ(F2->Desc->getNumElems(), 4u); |
88 | ASSERT_TRUE(F2->Desc->getElemSize() > 0); |
89 | |
90 | // Third field. |
91 | const Record::Field *F3 = SRecord->getField(I: 2u); |
92 | ASSERT_TRUE(F3); |
93 | ASSERT_FALSE(F3->isBitField()); |
94 | ASSERT_TRUE(F3->Desc->isArray()); |
95 | ASSERT_TRUE(F3->Desc->isCompositeArray()); |
96 | ASSERT_FALSE(F3->Desc->isPrimitiveArray()); |
97 | ASSERT_FALSE(F3->Desc->isPrimitive()); |
98 | ASSERT_TRUE(F3->Desc->ElemDesc); |
99 | ASSERT_EQ(F3->Desc->getNumElems(), 3u); |
100 | ASSERT_TRUE(F3->Desc->getElemSize() > 0); |
101 | |
102 | // Fourth field. |
103 | // Multidimensional arrays are treated as composite arrays, even |
104 | // if the value type is primitive. |
105 | const Record::Field *F4 = SRecord->getField(I: 3u); |
106 | ASSERT_TRUE(F4); |
107 | ASSERT_FALSE(F4->isBitField()); |
108 | ASSERT_TRUE(F4->Desc->isArray()); |
109 | ASSERT_TRUE(F4->Desc->isCompositeArray()); |
110 | ASSERT_FALSE(F4->Desc->isPrimitiveArray()); |
111 | ASSERT_FALSE(F4->Desc->isPrimitive()); |
112 | ASSERT_TRUE(F4->Desc->ElemDesc); |
113 | ASSERT_EQ(F4->Desc->getNumElems(), 3u); |
114 | ASSERT_TRUE(F4->Desc->getElemSize() > 0); |
115 | ASSERT_TRUE(F4->Desc->ElemDesc->isPrimitiveArray()); |
116 | |
117 | // Fifth field. Zero-size array. |
118 | const Record::Field *F5 = SRecord->getField(I: 4u); |
119 | ASSERT_TRUE(F5); |
120 | ASSERT_FALSE(F5->isBitField()); |
121 | ASSERT_TRUE(F5->Desc->isArray()); |
122 | ASSERT_FALSE(F5->Desc->isCompositeArray()); |
123 | ASSERT_TRUE(F5->Desc->isPrimitiveArray()); |
124 | ASSERT_FALSE(F5->Desc->isPrimitive()); |
125 | ASSERT_EQ(F5->Desc->getNumElems(), 0u); |
126 | |
127 | // Check pointer stuff. |
128 | // Global variables have an inline descriptor. |
129 | ASSERT_TRUE(GlobalPtr.isRoot()); |
130 | ASSERT_TRUE(GlobalPtr.isLive()); |
131 | ASSERT_FALSE(GlobalPtr.isZero()); |
132 | ASSERT_FALSE(GlobalPtr.isField()); |
133 | ASSERT_TRUE(GlobalPtr.getFieldDesc() == GlobalPtr.getDeclDesc()); |
134 | ASSERT_TRUE(GlobalPtr.getOffset() == 0); |
135 | ASSERT_FALSE(GlobalPtr.inArray()); |
136 | ASSERT_FALSE(GlobalPtr.isArrayElement()); |
137 | ASSERT_FALSE(GlobalPtr.isArrayRoot()); |
138 | ASSERT_FALSE(GlobalPtr.inPrimitiveArray()); |
139 | ASSERT_TRUE(GlobalPtr.isStatic()); |
140 | ASSERT_TRUE(GlobalPtr.isInitialized()); |
141 | ASSERT_FALSE(GlobalPtr.isOnePastEnd()); |
142 | ASSERT_FALSE(GlobalPtr.isElementPastEnd()); |
143 | |
144 | // Pointer to the first field (a primitive). |
145 | const Pointer &PF1 = GlobalPtr.atField(Off: F1->Offset); |
146 | ASSERT_TRUE(PF1.isLive()); |
147 | ASSERT_TRUE(PF1.isInitialized()); |
148 | ASSERT_TRUE(PF1.isField()); |
149 | ASSERT_FALSE(PF1.inArray()); |
150 | ASSERT_FALSE(PF1.isArrayElement()); |
151 | ASSERT_FALSE(PF1.isArrayRoot()); |
152 | ASSERT_FALSE(PF1.isOnePastEnd()); |
153 | ASSERT_FALSE(PF1.isRoot()); |
154 | ASSERT_TRUE(PF1.getFieldDesc()->isPrimitive()); |
155 | ASSERT_TRUE(Pointer::hasSameBase(PF1, GlobalPtr)); |
156 | ASSERT_TRUE(PF1.getBase() == GlobalPtr); |
157 | |
158 | // Pointer to the second field (a primitive array). |
159 | const Pointer &PF2 = GlobalPtr.atField(Off: F2->Offset); |
160 | ASSERT_TRUE(PF2.isLive()); |
161 | ASSERT_TRUE(PF2.isInitialized()); |
162 | ASSERT_TRUE(PF2.isField()); |
163 | ASSERT_TRUE(PF2.inArray()); |
164 | ASSERT_FALSE(PF2.isArrayElement()); |
165 | ASSERT_TRUE(PF2.isArrayRoot()); |
166 | ASSERT_TRUE(PF2.getNumElems() == 4); |
167 | ASSERT_FALSE(PF2.isOnePastEnd()); |
168 | ASSERT_FALSE(PF2.isRoot()); |
169 | ASSERT_FALSE(PF2.getFieldDesc()->isPrimitive()); |
170 | ASSERT_TRUE(PF2.getFieldDesc()->isArray()); |
171 | ASSERT_TRUE(Pointer::hasSameBase(PF2, GlobalPtr)); |
172 | ASSERT_TRUE(PF2.getBase() == GlobalPtr); |
173 | |
174 | // Check contents of field 2 (a primitive array). |
175 | { |
176 | const Pointer &E1 = PF2.atIndex(Idx: 0); |
177 | ASSERT_TRUE(E1.isLive()); |
178 | ASSERT_FALSE(E1.isArrayRoot()); |
179 | ASSERT_TRUE(E1.isArrayElement()); |
180 | ASSERT_TRUE(E1.inPrimitiveArray()); |
181 | ASSERT_TRUE(E1.deref<char>() == 'f'); |
182 | ASSERT_EQ(E1.getIndex(), 0u); |
183 | ASSERT_TRUE(E1 == E1.atIndex(0)); |
184 | ASSERT_TRUE(Pointer::hasSameBase(E1, GlobalPtr)); |
185 | |
186 | const Pointer &E2 = PF2.atIndex(Idx: 1); |
187 | ASSERT_TRUE(E2.isLive()); |
188 | ASSERT_FALSE(E2.isArrayRoot()); |
189 | ASSERT_TRUE(E2.isArrayElement()); |
190 | ASSERT_EQ(E2.getIndex(), 1u); |
191 | // Narrow() doesn't do anything on primitive array elements, as there is |
192 | // nothing to narrow into. |
193 | ASSERT_EQ(E2.narrow(), E2); |
194 | // ... so this should also hold. |
195 | ASSERT_EQ(E2.expand(), E2); |
196 | ASSERT_EQ(E2.narrow().expand(), E2); |
197 | |
198 | // .atIndex(1).atIndex(1) should be index 1. |
199 | ASSERT_EQ(PF2.atIndex(1).atIndex(1), PF2.atIndex(1)); |
200 | ASSERT_EQ(PF2.atIndex(1).narrow().atIndex(1), PF2.atIndex(1)); |
201 | |
202 | // getArray() should give us the array field again. |
203 | ASSERT_EQ(E2.getArray(), PF2); |
204 | |
205 | // One-after-the-end pointer. |
206 | const Pointer &O = PF2.atIndex(Idx: PF2.getNumElems()); |
207 | ASSERT_TRUE(O.isLive()); |
208 | ASSERT_TRUE(O.isOnePastEnd()); |
209 | ASSERT_TRUE(O.isInitialized()); |
210 | ASSERT_TRUE(O.getIndex() == PF2.getNumElems()); |
211 | } |
212 | |
213 | // Pointer to the third field (a composite array). |
214 | const Pointer &PF3 = GlobalPtr.atField(Off: F3->Offset); |
215 | ASSERT_TRUE(PF3.isLive()); |
216 | ASSERT_TRUE(PF3.isInitialized()); |
217 | ASSERT_TRUE(PF3.isField()); |
218 | ASSERT_TRUE(PF3.inArray()); |
219 | ASSERT_TRUE(PF3.isArrayRoot()); |
220 | ASSERT_FALSE(PF3.isArrayElement()); |
221 | ASSERT_TRUE(PF3.getNumElems() == 3); |
222 | ASSERT_FALSE(PF3.isOnePastEnd()); |
223 | ASSERT_FALSE(PF3.isRoot()); |
224 | ASSERT_FALSE(PF3.getFieldDesc()->isPrimitive()); |
225 | ASSERT_TRUE(PF3.getFieldDesc()->isArray()); |
226 | ASSERT_TRUE(Pointer::hasSameBase(PF3, GlobalPtr)); |
227 | ASSERT_TRUE(PF3.getBase() == GlobalPtr); |
228 | ASSERT_EQ(PF3.getRecord(), nullptr); |
229 | ASSERT_TRUE(PF3.getElemRecord()); |
230 | |
231 | // Check contents of field 3 (a composite array). |
232 | { |
233 | const Pointer &E1 = PF3.atIndex(Idx: 0); |
234 | // Note that we didn't call narrow() above, so this points |
235 | // to an array element and not just a field. |
236 | ASSERT_TRUE(E1.isLive()); |
237 | ASSERT_EQ(E1.getIndex(), 0); |
238 | ASSERT_TRUE(E1.isInitialized()); |
239 | ASSERT_TRUE(E1.isArrayElement()); |
240 | ASSERT_TRUE(E1.inArray()); |
241 | ASSERT_FALSE(E1.isArrayRoot()); |
242 | ASSERT_FALSE(E1.isRoot()); |
243 | ASSERT_EQ(E1.getArray(), PF3); |
244 | ASSERT_TRUE(E1.isField()); |
245 | ASSERT_TRUE(E1.getElemRecord()); |
246 | ASSERT_FALSE(E1.getRecord()); |
247 | |
248 | // Now the same with narrow(). |
249 | const Pointer &NE1 = PF3.atIndex(Idx: 0).narrow(); |
250 | ASSERT_NE(E1, NE1); |
251 | ASSERT_TRUE(NE1.isLive()); |
252 | ASSERT_EQ(NE1.getIndex(), 0); |
253 | ASSERT_TRUE(NE1.isInitialized()); |
254 | ASSERT_TRUE(NE1.isArrayElement()); |
255 | ASSERT_TRUE(NE1.isField()); |
256 | ASSERT_FALSE(NE1.inArray()); |
257 | ASSERT_FALSE(NE1.isArrayRoot()); |
258 | ASSERT_FALSE(NE1.isRoot()); |
259 | // Not possible, since this is narrow()ed: |
260 | // ASSERT_EQ(NE1.getArray(), PF3); |
261 | ASSERT_EQ(NE1.expand(), E1); |
262 | ASSERT_FALSE(NE1.getElemRecord()); |
263 | ASSERT_TRUE(NE1.getRecord()); |
264 | |
265 | // Second element, NOT narrowed. |
266 | const Pointer &E2 = PF3.atIndex(Idx: 1); |
267 | ASSERT_TRUE(E2.isLive()); |
268 | ASSERT_EQ(E2.getIndex(), 1); |
269 | ASSERT_TRUE(E2.isInitialized()); |
270 | ASSERT_TRUE(E2.isArrayElement()); |
271 | ASSERT_TRUE(E2.isField()); |
272 | ASSERT_TRUE(E2.inArray()); |
273 | ASSERT_FALSE(E2.isArrayRoot()); |
274 | ASSERT_FALSE(E2.isRoot()); |
275 | ASSERT_EQ(E2.getArray(), PF3); |
276 | |
277 | // Second element, narrowed. |
278 | const Pointer &NE2 = PF3.atIndex(Idx: 1).narrow(); |
279 | ASSERT_TRUE(NE2.isLive()); |
280 | ASSERT_EQ(NE2.getIndex(), 0); |
281 | ASSERT_TRUE(NE2.isInitialized()); |
282 | ASSERT_TRUE(NE2.isArrayElement()); |
283 | ASSERT_TRUE(NE2.isField()); |
284 | ASSERT_FALSE(NE2.inArray()); |
285 | ASSERT_FALSE(NE2.isArrayRoot()); |
286 | ASSERT_FALSE(NE2.isRoot()); |
287 | // Not possible, since this is narrow()ed: |
288 | // ASSERT_EQ(NE2.getArray(), PF3); |
289 | ASSERT_FALSE(NE2.getElemRecord()); |
290 | ASSERT_TRUE(NE2.getRecord()); |
291 | |
292 | // Chained atIndex() without narrowing in between. |
293 | ASSERT_EQ(PF3.atIndex(1).atIndex(1), PF3.atIndex(1)); |
294 | |
295 | // First field of the second element. |
296 | const Pointer &FP1 = NE2.atField(Off: NE2.getRecord()->getField(I: 0u)->Offset); |
297 | ASSERT_TRUE(FP1.isLive()); |
298 | ASSERT_TRUE(FP1.isInitialized()); |
299 | ASSERT_EQ(FP1.getBase(), NE2); |
300 | ASSERT_FALSE(FP1.isArrayElement()); |
301 | ASSERT_FALSE(FP1.inArray()); |
302 | ASSERT_FALSE(FP1.inPrimitiveArray()); |
303 | ASSERT_TRUE(FP1.isField()); |
304 | |
305 | // One-past-the-end of a composite array. |
306 | const Pointer &O = PF3.atIndex(Idx: PF3.getNumElems()).narrow(); |
307 | ASSERT_TRUE(O.isOnePastEnd()); |
308 | ASSERT_TRUE(O.isElementPastEnd()); |
309 | } |
310 | |
311 | // Pointer to the fourth field (a multidimensional primitive array). |
312 | const Pointer &PF4 = GlobalPtr.atField(Off: F4->Offset); |
313 | ASSERT_TRUE(PF4.isLive()); |
314 | ASSERT_TRUE(PF4.isInitialized()); |
315 | ASSERT_TRUE(PF4.isField()); |
316 | ASSERT_TRUE(PF4.inArray()); |
317 | ASSERT_TRUE(PF4.isArrayRoot()); |
318 | ASSERT_FALSE(PF4.isArrayElement()); |
319 | ASSERT_TRUE(PF4.getNumElems() == 3); |
320 | ASSERT_FALSE(PF4.isOnePastEnd()); |
321 | ASSERT_FALSE(PF4.isRoot()); |
322 | ASSERT_FALSE(PF4.getFieldDesc()->isPrimitive()); |
323 | ASSERT_TRUE(PF4.getFieldDesc()->isArray()); |
324 | ASSERT_TRUE(Pointer::hasSameBase(PF4, GlobalPtr)); |
325 | ASSERT_TRUE(PF4.getBase() == GlobalPtr); |
326 | ASSERT_EQ(PF4.getRecord(), nullptr); |
327 | ASSERT_EQ(PF4.getElemRecord(), nullptr); |
328 | ASSERT_NE(PF4.getField(), nullptr); |
329 | ASSERT_TRUE(PF4.getFieldDesc()->ElemDesc->isPrimitiveArray()); |
330 | // Check contents of field 4 (a primitive array). |
331 | { |
332 | // Pointer to the first element, is of type short[3]. |
333 | const Pointer &E1 = PF4.atIndex(Idx: 0); |
334 | ASSERT_NE(E1, PF4); |
335 | ASSERT_TRUE(E1.isLive()); |
336 | ASSERT_TRUE(E1.isArrayElement()); |
337 | ASSERT_TRUE(E1.inArray()); |
338 | ASSERT_EQ(E1.getNumElems(), 3u); |
339 | ASSERT_EQ(E1.getIndex(), 0u); |
340 | ASSERT_EQ(E1.getArray(), PF4); |
341 | |
342 | // Now narrow()'ed. |
343 | const Pointer &NE1 = PF4.atIndex(Idx: 0).narrow(); |
344 | ASSERT_NE(NE1, PF4); |
345 | ASSERT_NE(NE1, E1); |
346 | ASSERT_TRUE(NE1.isLive()); |
347 | ASSERT_TRUE(NE1.isArrayElement()); |
348 | ASSERT_TRUE(NE1.isArrayRoot()); |
349 | ASSERT_FALSE(NE1.getFieldDesc()->isCompositeArray()); |
350 | ASSERT_TRUE(NE1.getFieldDesc()->isPrimitiveArray()); |
351 | ASSERT_EQ(NE1.getFieldDesc()->getNumElems(), 3u); |
352 | ASSERT_TRUE(NE1.inArray()); |
353 | ASSERT_EQ(NE1.getNumElems(), 3u); |
354 | ASSERT_EQ(NE1.getIndex(), 0u); |
355 | |
356 | // Last element of the first dimension. |
357 | const Pointer &PE1 = PF4.atIndex(Idx: 0).narrow().atIndex(Idx: 2); |
358 | ASSERT_TRUE(PE1.isLive()); |
359 | ASSERT_EQ(PE1.deref<short>(), 3); |
360 | ASSERT_EQ(PE1.getArray(), NE1); |
361 | ASSERT_EQ(PE1.getIndex(), 2u); |
362 | |
363 | // third dimension |
364 | const Pointer &E3 = PF4.atIndex(Idx: 2); |
365 | ASSERT_NE(E3, PF4); |
366 | ASSERT_TRUE(E3.isLive()); |
367 | ASSERT_TRUE(E3.isArrayElement()); |
368 | ASSERT_FALSE(E3.isArrayRoot()); |
369 | ASSERT_TRUE(E3.inArray()); |
370 | ASSERT_EQ(E3.getNumElems(), 3u); |
371 | ASSERT_EQ(E3.getIndex(), 2u); |
372 | |
373 | // Same, but narrow()'ed. |
374 | const Pointer &NE3 = PF4.atIndex(Idx: 2).narrow(); |
375 | ASSERT_NE(NE3, PF4); |
376 | ASSERT_NE(NE3, E1); |
377 | ASSERT_TRUE(NE3.isLive()); |
378 | ASSERT_TRUE(NE3.isArrayElement()); |
379 | ASSERT_TRUE(NE3.isArrayRoot()); |
380 | ASSERT_FALSE(NE3.getFieldDesc()->isCompositeArray()); |
381 | ASSERT_TRUE(NE3.getFieldDesc()->isPrimitiveArray()); |
382 | ASSERT_EQ(NE3.getFieldDesc()->getNumElems(), 3u); |
383 | ASSERT_TRUE(NE3.inArray()); |
384 | ASSERT_EQ(NE3.getNumElems(), 3u); |
385 | // This is narrow()'ed, so not an "array elemnet" |
386 | ASSERT_EQ(PF4.atIndex(2).getIndex(), 2u); |
387 | ASSERT_EQ(NE3.getIndex(), 0u); |
388 | |
389 | // Last element of the last dimension |
390 | const Pointer &PE3 = PF4.atIndex(Idx: 2).narrow().atIndex(Idx: 2); |
391 | ASSERT_TRUE(PE3.isLive()); |
392 | ASSERT_EQ(PE3.deref<short>(), 9); |
393 | ASSERT_EQ(PE3.getArray(), NE3); |
394 | ASSERT_EQ(PE3.getIndex(), 2u); |
395 | } |
396 | |
397 | // Zero-size array. |
398 | { |
399 | const Pointer &PF5 = GlobalPtr.atField(Off: F5->Offset); |
400 | |
401 | ASSERT_TRUE(PF5.isZeroSizeArray()); |
402 | ASSERT_FALSE(PF5.isOnePastEnd()); |
403 | ASSERT_FALSE(PF5.isElementPastEnd()); |
404 | } |
405 | } |
406 | |