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