411 lines · cpp
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 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[] = "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, even104 // 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 is192 // 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 points235 // 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 dimension364 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 dimension390 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_TRUE(PF5.isOnePastEnd());403 ASSERT_FALSE(PF5.isElementPastEnd());404 405 const Pointer &E1 = PF5.atIndex(0);406 (void)E1;407 ASSERT_TRUE(PF5.isOnePastEnd());408 ASSERT_FALSE(PF5.isElementPastEnd());409 }410}411