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