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
11using namespace clang;
12using namespace clang::interp;
13using namespace clang::ast_matchers;
14
15/// Inspect generated Descriptors as well as the pointers we create.
16///
17TEST(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

source code of clang/unittests/AST/Interp/Descriptor.cpp