1 //===--- Pointer.cpp - Types for the constexpr VM ---------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "Pointer.h" 10 #include "Boolean.h" 11 #include "Context.h" 12 #include "Floating.h" 13 #include "Function.h" 14 #include "Integral.h" 15 #include "InterpBlock.h" 16 #include "MemberPointer.h" 17 #include "PrimType.h" 18 #include "Record.h" 19 #include "clang/AST/ExprCXX.h" 20 #include "clang/AST/RecordLayout.h" 21 22 using namespace clang; 23 using namespace clang::interp; 24 25 Pointer::Pointer(Block *Pointee) 26 : Pointer(Pointee, Pointee->getDescriptor()->getMetadataSize(), 27 Pointee->getDescriptor()->getMetadataSize()) {} 28 29 Pointer::Pointer(Block *Pointee, uint64_t BaseAndOffset) 30 : Pointer(Pointee, BaseAndOffset, BaseAndOffset) {} 31 32 Pointer::Pointer(const Pointer &P) 33 : Offset(P.Offset), PointeeStorage(P.PointeeStorage), 34 StorageKind(P.StorageKind) { 35 36 if (isBlockPointer() && PointeeStorage.BS.Pointee) 37 PointeeStorage.BS.Pointee->addPointer(this); 38 } 39 40 Pointer::Pointer(Block *Pointee, unsigned Base, uint64_t Offset) 41 : Offset(Offset), StorageKind(Storage::Block) { 42 assert((Base == RootPtrMark || Base % alignof(void *) == 0) && "wrong base"); 43 44 PointeeStorage.BS = {Pointee, Base}; 45 46 if (Pointee) 47 Pointee->addPointer(this); 48 } 49 50 Pointer::Pointer(Pointer &&P) 51 : Offset(P.Offset), PointeeStorage(P.PointeeStorage), 52 StorageKind(P.StorageKind) { 53 54 if (StorageKind == Storage::Block && PointeeStorage.BS.Pointee) 55 PointeeStorage.BS.Pointee->replacePointer(&P, this); 56 } 57 58 Pointer::~Pointer() { 59 if (!isBlockPointer()) 60 return; 61 62 if (Block *Pointee = PointeeStorage.BS.Pointee) { 63 Pointee->removePointer(this); 64 PointeeStorage.BS.Pointee = nullptr; 65 Pointee->cleanup(); 66 } 67 } 68 69 void Pointer::operator=(const Pointer &P) { 70 // If the current storage type is Block, we need to remove 71 // this pointer from the block. 72 if (isBlockPointer()) { 73 if (P.isBlockPointer() && this->block() == P.block()) { 74 Offset = P.Offset; 75 PointeeStorage.BS.Base = P.PointeeStorage.BS.Base; 76 return; 77 } 78 79 if (Block *Pointee = PointeeStorage.BS.Pointee) { 80 Pointee->removePointer(this); 81 PointeeStorage.BS.Pointee = nullptr; 82 Pointee->cleanup(); 83 } 84 } 85 86 StorageKind = P.StorageKind; 87 Offset = P.Offset; 88 89 if (P.isBlockPointer()) { 90 PointeeStorage.BS = P.PointeeStorage.BS; 91 PointeeStorage.BS.Pointee = P.PointeeStorage.BS.Pointee; 92 93 if (PointeeStorage.BS.Pointee) 94 PointeeStorage.BS.Pointee->addPointer(this); 95 } else if (P.isIntegralPointer()) { 96 PointeeStorage.Int = P.PointeeStorage.Int; 97 } else if (P.isFunctionPointer()) { 98 PointeeStorage.Fn = P.PointeeStorage.Fn; 99 } else { 100 assert(false && "Unhandled storage kind"); 101 } 102 } 103 104 void Pointer::operator=(Pointer &&P) { 105 // If the current storage type is Block, we need to remove 106 // this pointer from the block. 107 if (isBlockPointer()) { 108 if (P.isBlockPointer() && this->block() == P.block()) { 109 Offset = P.Offset; 110 PointeeStorage.BS.Base = P.PointeeStorage.BS.Base; 111 return; 112 } 113 114 if (Block *Pointee = PointeeStorage.BS.Pointee) { 115 assert(P.block() != this->block()); 116 Pointee->removePointer(this); 117 PointeeStorage.BS.Pointee = nullptr; 118 Pointee->cleanup(); 119 } 120 } 121 122 StorageKind = P.StorageKind; 123 Offset = P.Offset; 124 125 if (P.isBlockPointer()) { 126 PointeeStorage.BS = P.PointeeStorage.BS; 127 PointeeStorage.BS.Pointee = P.PointeeStorage.BS.Pointee; 128 129 if (PointeeStorage.BS.Pointee) 130 PointeeStorage.BS.Pointee->addPointer(this); 131 } else if (P.isIntegralPointer()) { 132 PointeeStorage.Int = P.PointeeStorage.Int; 133 } else if (P.isFunctionPointer()) { 134 PointeeStorage.Fn = P.PointeeStorage.Fn; 135 } else { 136 assert(false && "Unhandled storage kind"); 137 } 138 } 139 140 APValue Pointer::toAPValue(const ASTContext &ASTCtx) const { 141 llvm::SmallVector<APValue::LValuePathEntry, 5> Path; 142 143 if (isZero()) 144 return APValue(static_cast<const Expr *>(nullptr), CharUnits::Zero(), Path, 145 /*IsOnePastEnd=*/false, /*IsNullPtr=*/true); 146 if (isIntegralPointer()) 147 return APValue(static_cast<const Expr *>(nullptr), 148 CharUnits::fromQuantity(asIntPointer().Value + this->Offset), 149 Path, 150 /*IsOnePastEnd=*/false, /*IsNullPtr=*/false); 151 if (isFunctionPointer()) 152 return asFunctionPointer().toAPValue(ASTCtx); 153 154 // Build the lvalue base from the block. 155 const Descriptor *Desc = getDeclDesc(); 156 APValue::LValueBase Base; 157 if (const auto *VD = Desc->asValueDecl()) 158 Base = VD; 159 else if (const auto *E = Desc->asExpr()) { 160 // Create a DynamicAlloc base of the right type. 161 if (const auto *NewExpr = dyn_cast<CXXNewExpr>(E)) { 162 QualType AllocatedType; 163 if (NewExpr->isArray()) { 164 assert(Desc->isArray()); 165 APInt ArraySize(64, static_cast<uint64_t>(Desc->getNumElems()), 166 /*IsSigned=*/false); 167 AllocatedType = 168 ASTCtx.getConstantArrayType(NewExpr->getAllocatedType(), ArraySize, 169 nullptr, ArraySizeModifier::Normal, 0); 170 } else { 171 AllocatedType = NewExpr->getAllocatedType(); 172 } 173 // FIXME: Suboptimal counting of dynamic allocations. Move this to Context 174 // or InterpState? 175 static int ReportedDynamicAllocs = 0; 176 DynamicAllocLValue DA(ReportedDynamicAllocs++); 177 Base = APValue::LValueBase::getDynamicAlloc(DA, AllocatedType); 178 } else { 179 Base = E; 180 } 181 } else 182 llvm_unreachable("Invalid allocation type"); 183 184 if (isUnknownSizeArray()) 185 return APValue(Base, CharUnits::Zero(), Path, 186 /*IsOnePastEnd=*/isOnePastEnd(), /*IsNullPtr=*/false); 187 188 CharUnits Offset = CharUnits::Zero(); 189 190 auto getFieldOffset = [&](const FieldDecl *FD) -> CharUnits { 191 // This shouldn't happen, but if it does, don't crash inside 192 // getASTRecordLayout. 193 if (FD->getParent()->isInvalidDecl()) 194 return CharUnits::Zero(); 195 const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(FD->getParent()); 196 unsigned FieldIndex = FD->getFieldIndex(); 197 return ASTCtx.toCharUnitsFromBits(Layout.getFieldOffset(FieldIndex)); 198 }; 199 200 // Build the path into the object. 201 Pointer Ptr = *this; 202 while (Ptr.isField() || Ptr.isArrayElement()) { 203 if (Ptr.isArrayRoot()) { 204 Path.push_back(APValue::LValuePathEntry( 205 {Ptr.getFieldDesc()->asDecl(), /*IsVirtual=*/false})); 206 207 if (const auto *FD = 208 dyn_cast_if_present<FieldDecl>(Ptr.getFieldDesc()->asDecl())) 209 Offset += getFieldOffset(FD); 210 211 Ptr = Ptr.getBase(); 212 } else if (Ptr.isArrayElement()) { 213 Ptr = Ptr.expand(); 214 unsigned Index; 215 if (Ptr.isOnePastEnd()) 216 Index = Ptr.getArray().getNumElems(); 217 else 218 Index = Ptr.getIndex(); 219 220 QualType ElemType = Ptr.getFieldDesc()->getElemQualType(); 221 Offset += (Index * ASTCtx.getTypeSizeInChars(ElemType)); 222 223 Path.push_back(APValue::LValuePathEntry::ArrayIndex(Index)); 224 Ptr = Ptr.getArray(); 225 } else { 226 bool IsVirtual = false; 227 228 // Create a path entry for the field. 229 const Descriptor *Desc = Ptr.getFieldDesc(); 230 if (const auto *BaseOrMember = Desc->asDecl()) { 231 if (const auto *FD = dyn_cast<FieldDecl>(BaseOrMember)) { 232 Ptr = Ptr.getBase(); 233 Offset += getFieldOffset(FD); 234 } else if (const auto *RD = dyn_cast<CXXRecordDecl>(BaseOrMember)) { 235 IsVirtual = Ptr.isVirtualBaseClass(); 236 Ptr = Ptr.getBase(); 237 const Record *BaseRecord = Ptr.getRecord(); 238 239 const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout( 240 cast<CXXRecordDecl>(BaseRecord->getDecl())); 241 if (IsVirtual) 242 Offset += Layout.getVBaseClassOffset(RD); 243 else 244 Offset += Layout.getBaseClassOffset(RD); 245 246 } else { 247 Ptr = Ptr.getBase(); 248 } 249 Path.push_back(APValue::LValuePathEntry({BaseOrMember, IsVirtual})); 250 continue; 251 } 252 llvm_unreachable("Invalid field type"); 253 } 254 } 255 256 // FIXME(perf): We compute the lvalue path above, but we can't supply it 257 // for dummy pointers (that causes crashes later in CheckConstantExpression). 258 if (isDummy()) 259 Path.clear(); 260 261 // We assemble the LValuePath starting from the innermost pointer to the 262 // outermost one. SO in a.b.c, the first element in Path will refer to 263 // the field 'c', while later code expects it to refer to 'a'. 264 // Just invert the order of the elements. 265 std::reverse(Path.begin(), Path.end()); 266 267 return APValue(Base, Offset, Path, /*IsOnePastEnd=*/isOnePastEnd(), 268 /*IsNullPtr=*/false); 269 } 270 271 void Pointer::print(llvm::raw_ostream &OS) const { 272 switch (StorageKind) { 273 case Storage::Block: { 274 const Block *B = PointeeStorage.BS.Pointee; 275 OS << "(Block) " << B << " {"; 276 277 if (isRoot()) 278 OS << "rootptr(" << PointeeStorage.BS.Base << "), "; 279 else 280 OS << PointeeStorage.BS.Base << ", "; 281 282 if (isElementPastEnd()) 283 OS << "pastend, "; 284 else 285 OS << Offset << ", "; 286 287 if (B) 288 OS << B->getSize(); 289 else 290 OS << "nullptr"; 291 OS << "}"; 292 } break; 293 case Storage::Int: 294 OS << "(Int) {"; 295 OS << PointeeStorage.Int.Value << " + " << Offset << ", " 296 << PointeeStorage.Int.Desc; 297 OS << "}"; 298 break; 299 case Storage::Fn: 300 OS << "(Fn) { " << asFunctionPointer().getFunction() << " + " << Offset 301 << " }"; 302 } 303 } 304 305 std::string Pointer::toDiagnosticString(const ASTContext &Ctx) const { 306 if (isZero()) 307 return "nullptr"; 308 309 if (isIntegralPointer()) 310 return (Twine("&(") + Twine(asIntPointer().Value + Offset) + ")").str(); 311 312 return toAPValue(Ctx).getAsString(Ctx, getType()); 313 } 314 315 bool Pointer::isInitialized() const { 316 if (!isBlockPointer()) 317 return true; 318 319 if (isRoot() && PointeeStorage.BS.Base == sizeof(GlobalInlineDescriptor)) { 320 const GlobalInlineDescriptor &GD = 321 *reinterpret_cast<const GlobalInlineDescriptor *>(block()->rawData()); 322 return GD.InitState == GlobalInitState::Initialized; 323 } 324 325 assert(PointeeStorage.BS.Pointee && 326 "Cannot check if null pointer was initialized"); 327 const Descriptor *Desc = getFieldDesc(); 328 assert(Desc); 329 if (Desc->isPrimitiveArray()) { 330 if (isStatic() && PointeeStorage.BS.Base == 0) 331 return true; 332 333 InitMapPtr &IM = getInitMap(); 334 335 if (!IM) 336 return false; 337 338 if (IM->first) 339 return true; 340 341 return IM->second->isElementInitialized(getIndex()); 342 } 343 344 if (asBlockPointer().Base == 0) 345 return true; 346 347 // Field has its bit in an inline descriptor. 348 return getInlineDesc()->IsInitialized; 349 } 350 351 void Pointer::initialize() const { 352 if (!isBlockPointer()) 353 return; 354 355 assert(PointeeStorage.BS.Pointee && "Cannot initialize null pointer"); 356 const Descriptor *Desc = getFieldDesc(); 357 358 if (isRoot() && PointeeStorage.BS.Base == sizeof(GlobalInlineDescriptor)) { 359 GlobalInlineDescriptor &GD = *reinterpret_cast<GlobalInlineDescriptor *>( 360 asBlockPointer().Pointee->rawData()); 361 GD.InitState = GlobalInitState::Initialized; 362 return; 363 } 364 365 assert(Desc); 366 if (Desc->isPrimitiveArray()) { 367 // Primitive global arrays don't have an initmap. 368 if (isStatic() && PointeeStorage.BS.Base == 0) 369 return; 370 371 // Nothing to do for these. 372 if (Desc->getNumElems() == 0) 373 return; 374 375 InitMapPtr &IM = getInitMap(); 376 if (!IM) 377 IM = 378 std::make_pair(false, std::make_shared<InitMap>(Desc->getNumElems())); 379 380 assert(IM); 381 382 // All initialized. 383 if (IM->first) 384 return; 385 386 if (IM->second->initializeElement(getIndex())) { 387 IM->first = true; 388 IM->second.reset(); 389 } 390 return; 391 } 392 393 // Field has its bit in an inline descriptor. 394 assert(PointeeStorage.BS.Base != 0 && 395 "Only composite fields can be initialised"); 396 getInlineDesc()->IsInitialized = true; 397 } 398 399 void Pointer::activate() const { 400 // Field has its bit in an inline descriptor. 401 assert(PointeeStorage.BS.Base != 0 && 402 "Only composite fields can be activated"); 403 404 if (isRoot() && PointeeStorage.BS.Base == sizeof(GlobalInlineDescriptor)) 405 return; 406 if (!getInlineDesc()->InUnion) 407 return; 408 409 getInlineDesc()->IsActive = true; 410 411 // Get the union, iterate over its fields and DEactivate all others. 412 Pointer UnionPtr = getBase(); 413 while (!UnionPtr.getFieldDesc()->isUnion()) 414 UnionPtr = UnionPtr.getBase(); 415 416 const Record *UnionRecord = UnionPtr.getRecord(); 417 for (const Record::Field &F : UnionRecord->fields()) { 418 Pointer FieldPtr = UnionPtr.atField(F.Offset); 419 if (FieldPtr == *this) { 420 } else { 421 FieldPtr.getInlineDesc()->IsActive = false; 422 // FIXME: Recurse. 423 } 424 } 425 426 Pointer B = getBase(); 427 while (!B.isRoot() && B.inUnion()) { 428 // FIXME: Need to de-activate other fields of parent records. 429 B.getInlineDesc()->IsActive = true; 430 assert(B.isActive()); 431 B = B.getBase(); 432 } 433 } 434 435 void Pointer::deactivate() const { 436 // TODO: this only appears in constructors, so nothing to deactivate. 437 } 438 439 bool Pointer::hasSameBase(const Pointer &A, const Pointer &B) { 440 // Two null pointers always have the same base. 441 if (A.isZero() && B.isZero()) 442 return true; 443 444 if (A.isIntegralPointer() && B.isIntegralPointer()) 445 return true; 446 if (A.isFunctionPointer() && B.isFunctionPointer()) 447 return true; 448 449 if (A.isIntegralPointer() || B.isIntegralPointer()) 450 return A.getSource() == B.getSource(); 451 452 if (A.StorageKind != B.StorageKind) 453 return false; 454 455 return A.asBlockPointer().Pointee == B.asBlockPointer().Pointee; 456 } 457 458 bool Pointer::pointToSameBlock(const Pointer &A, const Pointer &B) { 459 if (!A.isBlockPointer() || !B.isBlockPointer()) 460 return false; 461 return A.block() == B.block(); 462 } 463 464 bool Pointer::hasSameArray(const Pointer &A, const Pointer &B) { 465 return hasSameBase(A, B) && 466 A.PointeeStorage.BS.Base == B.PointeeStorage.BS.Base && 467 A.getFieldDesc()->IsArray; 468 } 469 470 bool Pointer::pointsToLiteral() const { 471 if (isZero() || !isBlockPointer()) 472 return false; 473 474 const Expr *E = block()->getDescriptor()->asExpr(); 475 if (block()->isDynamic()) 476 return false; 477 478 return E && !isa<MaterializeTemporaryExpr, StringLiteral>(E); 479 } 480 481 std::optional<APValue> Pointer::toRValue(const Context &Ctx, 482 QualType ResultType) const { 483 const ASTContext &ASTCtx = Ctx.getASTContext(); 484 assert(!ResultType.isNull()); 485 // Method to recursively traverse composites. 486 std::function<bool(QualType, const Pointer &, APValue &)> Composite; 487 Composite = [&Composite, &Ctx, &ASTCtx](QualType Ty, const Pointer &Ptr, 488 APValue &R) { 489 if (const auto *AT = Ty->getAs<AtomicType>()) 490 Ty = AT->getValueType(); 491 492 // Invalid pointers. 493 if (Ptr.isDummy() || !Ptr.isLive() || !Ptr.isBlockPointer() || 494 Ptr.isPastEnd()) 495 return false; 496 497 // Primitive values. 498 if (std::optional<PrimType> T = Ctx.classify(Ty)) { 499 TYPE_SWITCH(*T, R = Ptr.deref<T>().toAPValue(ASTCtx)); 500 return true; 501 } 502 503 if (const auto *RT = Ty->getAs<RecordType>()) { 504 const auto *Record = Ptr.getRecord(); 505 assert(Record && "Missing record descriptor"); 506 507 bool Ok = true; 508 if (RT->getDecl()->isUnion()) { 509 const FieldDecl *ActiveField = nullptr; 510 APValue Value; 511 for (const auto &F : Record->fields()) { 512 const Pointer &FP = Ptr.atField(F.Offset); 513 QualType FieldTy = F.Decl->getType(); 514 if (FP.isActive()) { 515 if (std::optional<PrimType> T = Ctx.classify(FieldTy)) { 516 TYPE_SWITCH(*T, Value = FP.deref<T>().toAPValue(ASTCtx)); 517 } else { 518 Ok &= Composite(FieldTy, FP, Value); 519 } 520 ActiveField = FP.getFieldDesc()->asFieldDecl(); 521 break; 522 } 523 } 524 R = APValue(ActiveField, Value); 525 } else { 526 unsigned NF = Record->getNumFields(); 527 unsigned NB = Record->getNumBases(); 528 unsigned NV = Ptr.isBaseClass() ? 0 : Record->getNumVirtualBases(); 529 530 R = APValue(APValue::UninitStruct(), NB, NF); 531 532 for (unsigned I = 0; I < NF; ++I) { 533 const Record::Field *FD = Record->getField(I); 534 QualType FieldTy = FD->Decl->getType(); 535 const Pointer &FP = Ptr.atField(FD->Offset); 536 APValue &Value = R.getStructField(I); 537 538 if (std::optional<PrimType> T = Ctx.classify(FieldTy)) { 539 TYPE_SWITCH(*T, Value = FP.deref<T>().toAPValue(ASTCtx)); 540 } else { 541 Ok &= Composite(FieldTy, FP, Value); 542 } 543 } 544 545 for (unsigned I = 0; I < NB; ++I) { 546 const Record::Base *BD = Record->getBase(I); 547 QualType BaseTy = Ctx.getASTContext().getRecordType(BD->Decl); 548 const Pointer &BP = Ptr.atField(BD->Offset); 549 Ok &= Composite(BaseTy, BP, R.getStructBase(I)); 550 } 551 552 for (unsigned I = 0; I < NV; ++I) { 553 const Record::Base *VD = Record->getVirtualBase(I); 554 QualType VirtBaseTy = Ctx.getASTContext().getRecordType(VD->Decl); 555 const Pointer &VP = Ptr.atField(VD->Offset); 556 Ok &= Composite(VirtBaseTy, VP, R.getStructBase(NB + I)); 557 } 558 } 559 return Ok; 560 } 561 562 if (Ty->isIncompleteArrayType()) { 563 R = APValue(APValue::UninitArray(), 0, 0); 564 return true; 565 } 566 567 if (const auto *AT = Ty->getAsArrayTypeUnsafe()) { 568 const size_t NumElems = Ptr.getNumElems(); 569 QualType ElemTy = AT->getElementType(); 570 R = APValue(APValue::UninitArray{}, NumElems, NumElems); 571 572 bool Ok = true; 573 for (unsigned I = 0; I < NumElems; ++I) { 574 APValue &Slot = R.getArrayInitializedElt(I); 575 const Pointer &EP = Ptr.atIndex(I); 576 if (std::optional<PrimType> T = Ctx.classify(ElemTy)) { 577 TYPE_SWITCH(*T, Slot = EP.deref<T>().toAPValue(ASTCtx)); 578 } else { 579 Ok &= Composite(ElemTy, EP.narrow(), Slot); 580 } 581 } 582 return Ok; 583 } 584 585 // Complex types. 586 if (const auto *CT = Ty->getAs<ComplexType>()) { 587 QualType ElemTy = CT->getElementType(); 588 589 if (ElemTy->isIntegerType()) { 590 std::optional<PrimType> ElemT = Ctx.classify(ElemTy); 591 assert(ElemT); 592 INT_TYPE_SWITCH(*ElemT, { 593 auto V1 = Ptr.atIndex(0).deref<T>(); 594 auto V2 = Ptr.atIndex(1).deref<T>(); 595 R = APValue(V1.toAPSInt(), V2.toAPSInt()); 596 return true; 597 }); 598 } else if (ElemTy->isFloatingType()) { 599 R = APValue(Ptr.atIndex(0).deref<Floating>().getAPFloat(), 600 Ptr.atIndex(1).deref<Floating>().getAPFloat()); 601 return true; 602 } 603 return false; 604 } 605 606 // Vector types. 607 if (const auto *VT = Ty->getAs<VectorType>()) { 608 assert(Ptr.getFieldDesc()->isPrimitiveArray()); 609 QualType ElemTy = VT->getElementType(); 610 PrimType ElemT = *Ctx.classify(ElemTy); 611 612 SmallVector<APValue> Values; 613 Values.reserve(VT->getNumElements()); 614 for (unsigned I = 0; I != VT->getNumElements(); ++I) { 615 TYPE_SWITCH(ElemT, { 616 Values.push_back(Ptr.atIndex(I).deref<T>().toAPValue(ASTCtx)); 617 }); 618 } 619 620 assert(Values.size() == VT->getNumElements()); 621 R = APValue(Values.data(), Values.size()); 622 return true; 623 } 624 625 llvm_unreachable("invalid value to return"); 626 }; 627 628 // Invalid to read from. 629 if (isDummy() || !isLive() || isPastEnd()) 630 return std::nullopt; 631 632 // We can return these as rvalues, but we can't deref() them. 633 if (isZero() || isIntegralPointer()) 634 return toAPValue(ASTCtx); 635 636 // Just load primitive types. 637 if (std::optional<PrimType> T = Ctx.classify(ResultType)) { 638 TYPE_SWITCH(*T, return this->deref<T>().toAPValue(ASTCtx)); 639 } 640 641 // Return the composite type. 642 APValue Result; 643 if (!Composite(getType(), *this, Result)) 644 return std::nullopt; 645 return Result; 646 } 647 648 IntPointer IntPointer::atOffset(const ASTContext &ASTCtx, 649 unsigned Offset) const { 650 if (!this->Desc) 651 return *this; 652 const Record *R = this->Desc->ElemRecord; 653 if (!R) 654 return *this; 655 656 const Record::Field *F = nullptr; 657 for (auto &It : R->fields()) { 658 if (It.Offset == Offset) { 659 F = &It; 660 break; 661 } 662 } 663 if (!F) 664 return *this; 665 666 const FieldDecl *FD = F->Decl; 667 const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(FD->getParent()); 668 unsigned FieldIndex = FD->getFieldIndex(); 669 uint64_t FieldOffset = 670 ASTCtx.toCharUnitsFromBits(Layout.getFieldOffset(FieldIndex)) 671 .getQuantity(); 672 return IntPointer{F->Desc, this->Value + FieldOffset}; 673 } 674 675 IntPointer IntPointer::baseCast(const ASTContext &ASTCtx, 676 unsigned BaseOffset) const { 677 const Record *R = Desc->ElemRecord; 678 const Descriptor *BaseDesc = nullptr; 679 680 // This iterates over bases and checks for the proper offset. That's 681 // potentially slow but this case really shouldn't happen a lot. 682 for (const Record::Base &B : R->bases()) { 683 if (B.Offset == BaseOffset) { 684 BaseDesc = B.Desc; 685 break; 686 } 687 } 688 assert(BaseDesc); 689 690 // Adjust the offset value based on the information from the record layout. 691 const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(R->getDecl()); 692 CharUnits BaseLayoutOffset = 693 Layout.getBaseClassOffset(cast<CXXRecordDecl>(BaseDesc->asDecl())); 694 695 return {BaseDesc, Value + BaseLayoutOffset.getQuantity()}; 696 } 697