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"
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
[] =
19 "struct A { bool a; bool b; };\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";
29 auto AST
= tooling::buildASTFromCodeWithArgs(
30 Code
, {"-fexperimental-new-constant-interpreter"});
32 const VarDecl
*D
= selectFirst
<VarDecl
>(
33 "d", match(varDecl().bind("d"), AST
->getASTContext()));
34 ASSERT_NE(D
, nullptr);
36 const auto &Ctx
= AST
->getASTContext().getInterpContext();
37 Program
&Prog
= Ctx
.getProgram();
38 // Global is registered.
39 ASSERT_TRUE(Prog
.getGlobal(D
));
41 // Get a Pointer to the global.
42 const Pointer
&GlobalPtr
= Prog
.getPtrGlobal(*Prog
.getGlobal(D
));
44 // Test Descriptor of the struct S.
45 const Descriptor
*GlobalDesc
= GlobalPtr
.getFieldDesc();
46 ASSERT_TRUE(GlobalDesc
== GlobalPtr
.getDeclDesc());
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());
54 // Still true because this is a global variable.
55 ASSERT_TRUE(GlobalDesc
->getMetadataSize() == 0);
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());
64 // Test the Record for the struct S.
65 const Record
*SRecord
= GlobalDesc
->ElemRecord
;
67 ASSERT_TRUE(SRecord
->getNumFields() == 4);
68 ASSERT_TRUE(SRecord
->getNumBases() == 0);
69 ASSERT_FALSE(SRecord
->getDestructor());
72 const Record::Field
*F1
= SRecord
->getField(0u);
74 ASSERT_FALSE(F1
->isBitField());
75 ASSERT_TRUE(F1
->Desc
->isPrimitive());
78 const Record::Field
*F2
= SRecord
->getField(1u);
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);
90 const Record::Field
*F3
= SRecord
->getField(2u);
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);
102 // Multidimensional arrays are treated as composite arrays, even
103 // if the value type is primitive.
104 const Record::Field
*F4
= SRecord
->getField(3u);
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());
116 // Check pointer stuff.
117 // Global variables have no inline descriptor (yet).
118 ASSERT_TRUE(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());
133 // Pointer to the first field (a primitive).
134 const Pointer
&PF1
= GlobalPtr
.atField(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
);
147 // Pointer to the second field (a primitive array).
148 const Pointer
&PF2
= GlobalPtr
.atField(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
);
163 // Check contents of field 2 (a primitive array).
165 const Pointer
&E1
= PF2
.atIndex(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
));
175 const Pointer
&E2
= PF2
.atIndex(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
);
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));
191 // getArray() should give us the array field again.
192 ASSERT_EQ(E2
.getArray(), PF2
);
194 // One-after-the-end pointer.
195 const Pointer
&O
= PF2
.atIndex(PF2
.getNumElems());
196 ASSERT_TRUE(O
.isLive());
197 ASSERT_TRUE(O
.isOnePastEnd());
198 ASSERT_TRUE(O
.isInitialized());
199 ASSERT_TRUE(O
.getIndex() == PF2
.getNumElems());
202 // Pointer to the third field (a composite array).
203 const Pointer
&PF3
= GlobalPtr
.atField(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());
220 // Check contents of field 3 (a composite array).
222 const Pointer
&E1
= PF3
.atIndex(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());
237 // Now the same with narrow().
238 const Pointer
&NE1
= PF3
.atIndex(0).narrow();
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());
254 // Second element, NOT narrowed.
255 const Pointer
&E2
= PF3
.atIndex(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
);
266 // Second element, narrowed.
267 const Pointer
&NE2
= PF3
.atIndex(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());
281 // Chained atIndex() without narrowing in between.
282 ASSERT_EQ(PF3
.atIndex(1).atIndex(1), PF3
.atIndex(1));
284 // First field of the second element.
285 const Pointer
&FP1
= NE2
.atField(NE2
.getRecord()->getField(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());
294 // One-past-the-end of a composite array.
295 const Pointer
&O
= PF3
.atIndex(PF3
.getNumElems()).narrow();
296 ASSERT_TRUE(O
.isOnePastEnd());
297 ASSERT_TRUE(O
.isElementPastEnd());
300 // Pointer to the fourth field (a multidimensional primitive array).
301 const Pointer
&PF4
= GlobalPtr
.atField(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).
321 // Pointer to the first element, is of type short[3].
322 const Pointer
&E1
= PF4
.atIndex(0);
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
);
332 const Pointer
&NE1
= PF4
.atIndex(0).narrow();
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);
345 // Last element of the first dimension.
346 const Pointer
&PE1
= PF4
.atIndex(0).narrow().atIndex(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);
353 const Pointer
&E3
= PF4
.atIndex(2);
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);
362 // Same, but narrow()'ed.
363 const Pointer
&NE3
= PF4
.atIndex(2).narrow();
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);
378 // Last element of the last dimension
379 const Pointer
&PE3
= PF4
.atIndex(2).narrow().atIndex(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);