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 = dyn_cast<FieldDecl>(Ptr.getFieldDesc()->asDecl())) 208 Offset += getFieldOffset(FD); 209 210 Ptr = Ptr.getBase(); 211 } else if (Ptr.isArrayElement()) { 212 unsigned Index; 213 if (Ptr.isOnePastEnd()) 214 Index = Ptr.getArray().getNumElems(); 215 else 216 Index = Ptr.getIndex(); 217 218 Offset += (Index * ASTCtx.getTypeSizeInChars(Ptr.getType())); 219 Path.push_back(APValue::LValuePathEntry::ArrayIndex(Index)); 220 Ptr = Ptr.getArray(); 221 } else { 222 bool IsVirtual = false; 223 224 // Create a path entry for the field. 225 const Descriptor *Desc = Ptr.getFieldDesc(); 226 if (const auto *BaseOrMember = Desc->asDecl()) { 227 if (const auto *FD = dyn_cast<FieldDecl>(BaseOrMember)) { 228 Ptr = Ptr.getBase(); 229 Offset += getFieldOffset(FD); 230 } else if (const auto *RD = dyn_cast<CXXRecordDecl>(BaseOrMember)) { 231 IsVirtual = Ptr.isVirtualBaseClass(); 232 Ptr = Ptr.getBase(); 233 const Record *BaseRecord = Ptr.getRecord(); 234 235 const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout( 236 cast<CXXRecordDecl>(BaseRecord->getDecl())); 237 if (IsVirtual) 238 Offset += Layout.getVBaseClassOffset(RD); 239 else 240 Offset += Layout.getBaseClassOffset(RD); 241 242 } else { 243 Ptr = Ptr.getBase(); 244 } 245 Path.push_back(APValue::LValuePathEntry({BaseOrMember, IsVirtual})); 246 continue; 247 } 248 llvm_unreachable("Invalid field type"); 249 } 250 } 251 252 // FIXME(perf): We compute the lvalue path above, but we can't supply it 253 // for dummy pointers (that causes crashes later in CheckConstantExpression). 254 if (isDummy()) 255 Path.clear(); 256 257 // We assemble the LValuePath starting from the innermost pointer to the 258 // outermost one. SO in a.b.c, the first element in Path will refer to 259 // the field 'c', while later code expects it to refer to 'a'. 260 // Just invert the order of the elements. 261 std::reverse(Path.begin(), Path.end()); 262 263 return APValue(Base, Offset, Path, /*IsOnePastEnd=*/isOnePastEnd(), 264 /*IsNullPtr=*/false); 265 } 266 267 void Pointer::print(llvm::raw_ostream &OS) const { 268 switch (StorageKind) { 269 case Storage::Block: { 270 const Block *B = PointeeStorage.BS.Pointee; 271 OS << "(Block) " << B << " {"; 272 273 if (isRoot()) 274 OS << "rootptr(" << PointeeStorage.BS.Base << "), "; 275 else 276 OS << PointeeStorage.BS.Base << ", "; 277 278 if (isElementPastEnd()) 279 OS << "pastend, "; 280 else 281 OS << Offset << ", "; 282 283 if (B) 284 OS << B->getSize(); 285 else 286 OS << "nullptr"; 287 OS << "}"; 288 } break; 289 case Storage::Int: 290 OS << "(Int) {"; 291 OS << PointeeStorage.Int.Value << " + " << Offset << ", " 292 << PointeeStorage.Int.Desc; 293 OS << "}"; 294 break; 295 case Storage::Fn: 296 OS << "(Fn) { " << asFunctionPointer().getFunction() << " + " << Offset 297 << " }"; 298 } 299 } 300 301 std::string Pointer::toDiagnosticString(const ASTContext &Ctx) const { 302 if (isZero()) 303 return "nullptr"; 304 305 if (isIntegralPointer()) 306 return (Twine("&(") + Twine(asIntPointer().Value + Offset) + ")").str(); 307 308 return toAPValue(Ctx).getAsString(Ctx, getType()); 309 } 310 311 bool Pointer::isInitialized() const { 312 if (!isBlockPointer()) 313 return true; 314 315 if (isRoot() && PointeeStorage.BS.Base == sizeof(GlobalInlineDescriptor)) { 316 const GlobalInlineDescriptor &GD = 317 *reinterpret_cast<const GlobalInlineDescriptor *>(block()->rawData()); 318 return GD.InitState == GlobalInitState::Initialized; 319 } 320 321 assert(PointeeStorage.BS.Pointee && 322 "Cannot check if null pointer was initialized"); 323 const Descriptor *Desc = getFieldDesc(); 324 assert(Desc); 325 if (Desc->isPrimitiveArray()) { 326 if (isStatic() && PointeeStorage.BS.Base == 0) 327 return true; 328 329 InitMapPtr &IM = getInitMap(); 330 331 if (!IM) 332 return false; 333 334 if (IM->first) 335 return true; 336 337 return IM->second->isElementInitialized(getIndex()); 338 } 339 340 if (asBlockPointer().Base == 0) 341 return true; 342 343 // Field has its bit in an inline descriptor. 344 return getInlineDesc()->IsInitialized; 345 } 346 347 void Pointer::initialize() const { 348 if (!isBlockPointer()) 349 return; 350 351 assert(PointeeStorage.BS.Pointee && "Cannot initialize null pointer"); 352 const Descriptor *Desc = getFieldDesc(); 353 354 if (isRoot() && PointeeStorage.BS.Base == sizeof(GlobalInlineDescriptor)) { 355 GlobalInlineDescriptor &GD = *reinterpret_cast<GlobalInlineDescriptor *>( 356 asBlockPointer().Pointee->rawData()); 357 GD.InitState = GlobalInitState::Initialized; 358 return; 359 } 360 361 assert(Desc); 362 if (Desc->isPrimitiveArray()) { 363 // Primitive global arrays don't have an initmap. 364 if (isStatic() && PointeeStorage.BS.Base == 0) 365 return; 366 367 // Nothing to do for these. 368 if (Desc->getNumElems() == 0) 369 return; 370 371 InitMapPtr &IM = getInitMap(); 372 if (!IM) 373 IM = 374 std::make_pair(false, std::make_shared<InitMap>(Desc->getNumElems())); 375 376 assert(IM); 377 378 // All initialized. 379 if (IM->first) 380 return; 381 382 if (IM->second->initializeElement(getIndex())) { 383 IM->first = true; 384 IM->second.reset(); 385 } 386 return; 387 } 388 389 // Field has its bit in an inline descriptor. 390 assert(PointeeStorage.BS.Base != 0 && 391 "Only composite fields can be initialised"); 392 getInlineDesc()->IsInitialized = true; 393 } 394 395 void Pointer::activate() const { 396 // Field has its bit in an inline descriptor. 397 assert(PointeeStorage.BS.Base != 0 && 398 "Only composite fields can be activated"); 399 400 if (isRoot() && PointeeStorage.BS.Base == sizeof(GlobalInlineDescriptor)) 401 return; 402 if (!getInlineDesc()->InUnion) 403 return; 404 405 getInlineDesc()->IsActive = true; 406 407 // Get the union, iterate over its fields and DEactivate all others. 408 Pointer UnionPtr = getBase(); 409 while (!UnionPtr.getFieldDesc()->isUnion()) 410 UnionPtr = UnionPtr.getBase(); 411 412 const Record *UnionRecord = UnionPtr.getRecord(); 413 for (const Record::Field &F : UnionRecord->fields()) { 414 Pointer FieldPtr = UnionPtr.atField(F.Offset); 415 if (FieldPtr == *this) { 416 } else { 417 FieldPtr.getInlineDesc()->IsActive = false; 418 // FIXME: Recurse. 419 } 420 } 421 422 Pointer B = getBase(); 423 while (!B.isRoot() && B.inUnion()) { 424 // FIXME: Need to de-activate other fields of parent records. 425 B.getInlineDesc()->IsActive = true; 426 assert(B.isActive()); 427 B = B.getBase(); 428 } 429 } 430 431 void Pointer::deactivate() const { 432 // TODO: this only appears in constructors, so nothing to deactivate. 433 } 434 435 bool Pointer::hasSameBase(const Pointer &A, const Pointer &B) { 436 // Two null pointers always have the same base. 437 if (A.isZero() && B.isZero()) 438 return true; 439 440 if (A.isIntegralPointer() && B.isIntegralPointer()) 441 return true; 442 if (A.isFunctionPointer() && B.isFunctionPointer()) 443 return true; 444 445 if (A.isIntegralPointer() || B.isIntegralPointer()) 446 return A.getSource() == B.getSource(); 447 448 if (A.StorageKind != B.StorageKind) 449 return false; 450 451 return A.asBlockPointer().Pointee == B.asBlockPointer().Pointee; 452 } 453 454 bool Pointer::pointToSameBlock(const Pointer &A, const Pointer &B) { 455 if (!A.isBlockPointer() || !B.isBlockPointer()) 456 return false; 457 return A.block() == B.block(); 458 } 459 460 bool Pointer::hasSameArray(const Pointer &A, const Pointer &B) { 461 return hasSameBase(A, B) && 462 A.PointeeStorage.BS.Base == B.PointeeStorage.BS.Base && 463 A.getFieldDesc()->IsArray; 464 } 465 466 bool Pointer::pointsToLiteral() const { 467 if (isZero() || !isBlockPointer()) 468 return false; 469 470 const Expr *E = block()->getDescriptor()->asExpr(); 471 if (block()->isDynamic()) 472 return false; 473 474 return E && !isa<MaterializeTemporaryExpr, StringLiteral>(E); 475 } 476 477 std::optional<APValue> Pointer::toRValue(const Context &Ctx, 478 QualType ResultType) const { 479 const ASTContext &ASTCtx = Ctx.getASTContext(); 480 assert(!ResultType.isNull()); 481 // Method to recursively traverse composites. 482 std::function<bool(QualType, const Pointer &, APValue &)> Composite; 483 Composite = [&Composite, &Ctx, &ASTCtx](QualType Ty, const Pointer &Ptr, 484 APValue &R) { 485 if (const auto *AT = Ty->getAs<AtomicType>()) 486 Ty = AT->getValueType(); 487 488 // Invalid pointers. 489 if (Ptr.isDummy() || !Ptr.isLive() || !Ptr.isBlockPointer() || 490 Ptr.isPastEnd()) 491 return false; 492 493 // Primitive values. 494 if (std::optional<PrimType> T = Ctx.classify(Ty)) { 495 TYPE_SWITCH(*T, R = Ptr.deref<T>().toAPValue(ASTCtx)); 496 return true; 497 } 498 499 if (const auto *RT = Ty->getAs<RecordType>()) { 500 const auto *Record = Ptr.getRecord(); 501 assert(Record && "Missing record descriptor"); 502 503 bool Ok = true; 504 if (RT->getDecl()->isUnion()) { 505 const FieldDecl *ActiveField = nullptr; 506 APValue Value; 507 for (const auto &F : Record->fields()) { 508 const Pointer &FP = Ptr.atField(F.Offset); 509 QualType FieldTy = F.Decl->getType(); 510 if (FP.isActive()) { 511 if (std::optional<PrimType> T = Ctx.classify(FieldTy)) { 512 TYPE_SWITCH(*T, Value = FP.deref<T>().toAPValue(ASTCtx)); 513 } else { 514 Ok &= Composite(FieldTy, FP, Value); 515 } 516 ActiveField = FP.getFieldDesc()->asFieldDecl(); 517 break; 518 } 519 } 520 R = APValue(ActiveField, Value); 521 } else { 522 unsigned NF = Record->getNumFields(); 523 unsigned NB = Record->getNumBases(); 524 unsigned NV = Ptr.isBaseClass() ? 0 : Record->getNumVirtualBases(); 525 526 R = APValue(APValue::UninitStruct(), NB, NF); 527 528 for (unsigned I = 0; I < NF; ++I) { 529 const Record::Field *FD = Record->getField(I); 530 QualType FieldTy = FD->Decl->getType(); 531 const Pointer &FP = Ptr.atField(FD->Offset); 532 APValue &Value = R.getStructField(I); 533 534 if (std::optional<PrimType> T = Ctx.classify(FieldTy)) { 535 TYPE_SWITCH(*T, Value = FP.deref<T>().toAPValue(ASTCtx)); 536 } else { 537 Ok &= Composite(FieldTy, FP, Value); 538 } 539 } 540 541 for (unsigned I = 0; I < NB; ++I) { 542 const Record::Base *BD = Record->getBase(I); 543 QualType BaseTy = Ctx.getASTContext().getRecordType(BD->Decl); 544 const Pointer &BP = Ptr.atField(BD->Offset); 545 Ok &= Composite(BaseTy, BP, R.getStructBase(I)); 546 } 547 548 for (unsigned I = 0; I < NV; ++I) { 549 const Record::Base *VD = Record->getVirtualBase(I); 550 QualType VirtBaseTy = Ctx.getASTContext().getRecordType(VD->Decl); 551 const Pointer &VP = Ptr.atField(VD->Offset); 552 Ok &= Composite(VirtBaseTy, VP, R.getStructBase(NB + I)); 553 } 554 } 555 return Ok; 556 } 557 558 if (Ty->isIncompleteArrayType()) { 559 R = APValue(APValue::UninitArray(), 0, 0); 560 return true; 561 } 562 563 if (const auto *AT = Ty->getAsArrayTypeUnsafe()) { 564 const size_t NumElems = Ptr.getNumElems(); 565 QualType ElemTy = AT->getElementType(); 566 R = APValue(APValue::UninitArray{}, NumElems, NumElems); 567 568 bool Ok = true; 569 for (unsigned I = 0; I < NumElems; ++I) { 570 APValue &Slot = R.getArrayInitializedElt(I); 571 const Pointer &EP = Ptr.atIndex(I); 572 if (std::optional<PrimType> T = Ctx.classify(ElemTy)) { 573 TYPE_SWITCH(*T, Slot = EP.deref<T>().toAPValue(ASTCtx)); 574 } else { 575 Ok &= Composite(ElemTy, EP.narrow(), Slot); 576 } 577 } 578 return Ok; 579 } 580 581 // Complex types. 582 if (const auto *CT = Ty->getAs<ComplexType>()) { 583 QualType ElemTy = CT->getElementType(); 584 585 if (ElemTy->isIntegerType()) { 586 std::optional<PrimType> ElemT = Ctx.classify(ElemTy); 587 assert(ElemT); 588 INT_TYPE_SWITCH(*ElemT, { 589 auto V1 = Ptr.atIndex(0).deref<T>(); 590 auto V2 = Ptr.atIndex(1).deref<T>(); 591 R = APValue(V1.toAPSInt(), V2.toAPSInt()); 592 return true; 593 }); 594 } else if (ElemTy->isFloatingType()) { 595 R = APValue(Ptr.atIndex(0).deref<Floating>().getAPFloat(), 596 Ptr.atIndex(1).deref<Floating>().getAPFloat()); 597 return true; 598 } 599 return false; 600 } 601 602 // Vector types. 603 if (const auto *VT = Ty->getAs<VectorType>()) { 604 assert(Ptr.getFieldDesc()->isPrimitiveArray()); 605 QualType ElemTy = VT->getElementType(); 606 PrimType ElemT = *Ctx.classify(ElemTy); 607 608 SmallVector<APValue> Values; 609 Values.reserve(VT->getNumElements()); 610 for (unsigned I = 0; I != VT->getNumElements(); ++I) { 611 TYPE_SWITCH(ElemT, { 612 Values.push_back(Ptr.atIndex(I).deref<T>().toAPValue(ASTCtx)); 613 }); 614 } 615 616 assert(Values.size() == VT->getNumElements()); 617 R = APValue(Values.data(), Values.size()); 618 return true; 619 } 620 621 llvm_unreachable("invalid value to return"); 622 }; 623 624 // Invalid to read from. 625 if (isDummy() || !isLive() || isPastEnd()) 626 return std::nullopt; 627 628 // We can return these as rvalues, but we can't deref() them. 629 if (isZero() || isIntegralPointer()) 630 return toAPValue(ASTCtx); 631 632 // Just load primitive types. 633 if (std::optional<PrimType> T = Ctx.classify(ResultType)) { 634 TYPE_SWITCH(*T, return this->deref<T>().toAPValue(ASTCtx)); 635 } 636 637 // Return the composite type. 638 APValue Result; 639 if (!Composite(getType(), *this, Result)) 640 return std::nullopt; 641 return Result; 642 } 643 644 IntPointer IntPointer::atOffset(const ASTContext &ASTCtx, 645 unsigned Offset) const { 646 if (!this->Desc) 647 return *this; 648 const Record *R = this->Desc->ElemRecord; 649 if (!R) 650 return *this; 651 652 const Record::Field *F = nullptr; 653 for (auto &It : R->fields()) { 654 if (It.Offset == Offset) { 655 F = &It; 656 break; 657 } 658 } 659 if (!F) 660 return *this; 661 662 const FieldDecl *FD = F->Decl; 663 const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(FD->getParent()); 664 unsigned FieldIndex = FD->getFieldIndex(); 665 uint64_t FieldOffset = 666 ASTCtx.toCharUnitsFromBits(Layout.getFieldOffset(FieldIndex)) 667 .getQuantity(); 668 return IntPointer{this->Desc, this->Value + FieldOffset}; 669 } 670