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"
11 using namespace clang
;
12 using namespace clang::interp
;
13 using namespace clang::ast_matchers
;
15 /// Inspect generated Descriptors as well as the pointers we create.
17 TEST(Descriptor
, Primitives
) {
18 constexpr char Code
[] = "struct A { bool a; bool b; };\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";
30 auto AST
= tooling::buildASTFromCodeWithArgs(
31 Code
, {"-fexperimental-new-constant-interpreter"});
33 const VarDecl
*D
= selectFirst
<VarDecl
>(
34 "d", match(varDecl().bind("d"), AST
->getASTContext()));
35 ASSERT_NE(D
, nullptr);
37 const auto &Ctx
= AST
->getASTContext().getInterpContext();
38 Program
&Prog
= Ctx
.getProgram();
39 // Global is registered.
40 ASSERT_TRUE(Prog
.getGlobal(D
));
42 // Get a Pointer to the global.
43 const Pointer
&GlobalPtr
= Prog
.getPtrGlobal(*Prog
.getGlobal(D
));
45 // Test Descriptor of the struct S.
46 const Descriptor
*GlobalDesc
= GlobalPtr
.getFieldDesc();
47 ASSERT_TRUE(GlobalDesc
== GlobalPtr
.getDeclDesc());
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());
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());
65 // Test the Record for the struct S.
66 const Record
*SRecord
= GlobalDesc
->ElemRecord
;
68 ASSERT_TRUE(SRecord
->getNumFields() == 5);
69 ASSERT_TRUE(SRecord
->getNumBases() == 0);
70 ASSERT_FALSE(SRecord
->getDestructor());
73 const Record::Field
*F1
= SRecord
->getField(0u);
75 ASSERT_FALSE(F1
->isBitField());
76 ASSERT_TRUE(F1
->Desc
->isPrimitive());
79 const Record::Field
*F2
= SRecord
->getField(1u);
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);
91 const Record::Field
*F3
= SRecord
->getField(2u);
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);
103 // Multidimensional arrays are treated as composite arrays, even
104 // if the value type is primitive.
105 const Record::Field
*F4
= SRecord
->getField(3u);
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());
117 // Fifth field. Zero-size array.
118 const Record::Field
*F5
= SRecord
->getField(4u);
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);
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());
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
);
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
);
174 // Check contents of field 2 (a primitive array).
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
));
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
);
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));
202 // getArray() should give us the array field again.
203 ASSERT_EQ(E2
.getArray(), PF2
);
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());
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());
231 // Check contents of field 3 (a composite array).
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());
248 // Now the same with narrow().
249 const Pointer
&NE1
= PF3
.atIndex(0).narrow();
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());
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
);
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());
292 // Chained atIndex() without narrowing in between.
293 ASSERT_EQ(PF3
.atIndex(1).atIndex(1), PF3
.atIndex(1));
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());
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());
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).
332 // Pointer to the first element, is of type short[3].
333 const Pointer
&E1
= PF4
.atIndex(0);
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
);
343 const Pointer
&NE1
= PF4
.atIndex(0).narrow();
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);
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);
364 const Pointer
&E3
= PF4
.atIndex(2);
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);
373 // Same, but narrow()'ed.
374 const Pointer
&NE3
= PF4
.atIndex(2).narrow();
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);
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);
399 const Pointer
&PF5
= GlobalPtr
.atField(F5
->Offset
);
401 ASSERT_TRUE(PF5
.isZeroSizeArray());
402 ASSERT_FALSE(PF5
.isOnePastEnd());
403 ASSERT_FALSE(PF5
.isElementPastEnd());