xref: /llvm-project/clang/unittests/AST/ByteCode/Descriptor.cpp (revision 800b07396ff54b037fa9b73bb15586456656fb79)
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, {"-fexperimental-new-constant-interpreter"});
32 
33   const VarDecl *D = selectFirst<VarDecl>(
34       "d", match(varDecl().bind("d"), 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(*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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(NE2.getRecord()->getField(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(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(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(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(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(0).narrow().atIndex(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(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(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(2).narrow().atIndex(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(F5->Offset);
400 
401     ASSERT_TRUE(PF5.isZeroSizeArray());
402     ASSERT_FALSE(PF5.isOnePastEnd());
403     ASSERT_FALSE(PF5.isElementPastEnd());
404   }
405 }
406