1 //===- ExternalASTSource.h - Abstract External AST Interface ----*- 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 // This file defines the ExternalASTSource interface, which enables 10 // construction of AST nodes from some external source. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #ifndef LLVM_CLANG_AST_EXTERNALASTSOURCE_H 15 #define LLVM_CLANG_AST_EXTERNALASTSOURCE_H 16 17 #include "clang/AST/CharUnits.h" 18 #include "clang/AST/DeclBase.h" 19 #include "clang/Basic/LLVM.h" 20 #include "llvm/ADT/ArrayRef.h" 21 #include "llvm/ADT/DenseMap.h" 22 #include "llvm/ADT/IntrusiveRefCntPtr.h" 23 #include "llvm/ADT/PointerUnion.h" 24 #include "llvm/ADT/STLExtras.h" 25 #include "llvm/ADT/SmallVector.h" 26 #include "llvm/ADT/iterator.h" 27 #include "llvm/Support/PointerLikeTypeTraits.h" 28 #include <algorithm> 29 #include <cassert> 30 #include <cstddef> 31 #include <cstdint> 32 #include <iterator> 33 #include <new> 34 #include <optional> 35 #include <utility> 36 37 namespace clang { 38 39 class ASTConsumer; 40 class ASTContext; 41 class ASTSourceDescriptor; 42 class CXXBaseSpecifier; 43 class CXXCtorInitializer; 44 class CXXRecordDecl; 45 class DeclarationName; 46 class FieldDecl; 47 class IdentifierInfo; 48 class NamedDecl; 49 class ObjCInterfaceDecl; 50 class RecordDecl; 51 class Selector; 52 class Stmt; 53 class TagDecl; 54 55 /// Abstract interface for external sources of AST nodes. 56 /// 57 /// External AST sources provide AST nodes constructed from some 58 /// external source, such as a precompiled header. External AST 59 /// sources can resolve types and declarations from abstract IDs into 60 /// actual type and declaration nodes, and read parts of declaration 61 /// contexts. 62 class ExternalASTSource : public RefCountedBase<ExternalASTSource> { 63 friend class ExternalSemaSource; 64 65 /// Generation number for this external AST source. Must be increased 66 /// whenever we might have added new redeclarations for existing decls. 67 uint32_t CurrentGeneration = 0; 68 69 /// LLVM-style RTTI. 70 static char ID; 71 72 public: 73 ExternalASTSource() = default; 74 virtual ~ExternalASTSource(); 75 76 /// RAII class for safely pairing a StartedDeserializing call 77 /// with FinishedDeserializing. 78 class Deserializing { 79 ExternalASTSource *Source; 80 81 public: Deserializing(ExternalASTSource * source)82 explicit Deserializing(ExternalASTSource *source) : Source(source) { 83 assert(Source); 84 Source->StartedDeserializing(); 85 } 86 ~Deserializing()87 ~Deserializing() { 88 Source->FinishedDeserializing(); 89 } 90 }; 91 92 /// Get the current generation of this AST source. This number 93 /// is incremented each time the AST source lazily extends an existing 94 /// entity. getGeneration()95 uint32_t getGeneration() const { return CurrentGeneration; } 96 97 /// Resolve a declaration ID into a declaration, potentially 98 /// building a new declaration. 99 /// 100 /// This method only needs to be implemented if the AST source ever 101 /// passes back decl sets as VisibleDeclaration objects. 102 /// 103 /// The default implementation of this method is a no-op. 104 virtual Decl *GetExternalDecl(GlobalDeclID ID); 105 106 /// Resolve a selector ID into a selector. 107 /// 108 /// This operation only needs to be implemented if the AST source 109 /// returns non-zero for GetNumKnownSelectors(). 110 /// 111 /// The default implementation of this method is a no-op. 112 virtual Selector GetExternalSelector(uint32_t ID); 113 114 /// Returns the number of selectors known to the external AST 115 /// source. 116 /// 117 /// The default implementation of this method is a no-op. 118 virtual uint32_t GetNumExternalSelectors(); 119 120 /// Resolve the offset of a statement in the decl stream into 121 /// a statement. 122 /// 123 /// This operation is meant to be used via a LazyOffsetPtr. It only 124 /// needs to be implemented if the AST source uses methods like 125 /// FunctionDecl::setLazyBody when building decls. 126 /// 127 /// The default implementation of this method is a no-op. 128 virtual Stmt *GetExternalDeclStmt(uint64_t Offset); 129 130 /// Resolve the offset of a set of C++ constructor initializers in 131 /// the decl stream into an array of initializers. 132 /// 133 /// The default implementation of this method is a no-op. 134 virtual CXXCtorInitializer **GetExternalCXXCtorInitializers(uint64_t Offset); 135 136 /// Resolve the offset of a set of C++ base specifiers in the decl 137 /// stream into an array of specifiers. 138 /// 139 /// The default implementation of this method is a no-op. 140 virtual CXXBaseSpecifier *GetExternalCXXBaseSpecifiers(uint64_t Offset); 141 142 /// Update an out-of-date identifier. updateOutOfDateIdentifier(const IdentifierInfo & II)143 virtual void updateOutOfDateIdentifier(const IdentifierInfo &II) {} 144 145 /// Find all declarations with the given name in the given context, 146 /// and add them to the context by calling SetExternalVisibleDeclsForName 147 /// or SetNoExternalVisibleDeclsForName. 148 /// \return \c true if any declarations might have been found, \c false if 149 /// we definitely have no declarations with tbis name. 150 /// 151 /// The default implementation of this method is a no-op returning \c false. 152 virtual bool 153 FindExternalVisibleDeclsByName(const DeclContext *DC, DeclarationName Name); 154 155 /// Ensures that the table of all visible declarations inside this 156 /// context is up to date. 157 /// 158 /// The default implementation of this function is a no-op. 159 virtual void completeVisibleDeclsMap(const DeclContext *DC); 160 161 /// Retrieve the module that corresponds to the given module ID. getModule(unsigned ID)162 virtual Module *getModule(unsigned ID) { return nullptr; } 163 164 /// Return a descriptor for the corresponding module, if one exists. 165 virtual std::optional<ASTSourceDescriptor> getSourceDescriptor(unsigned ID); 166 167 enum ExtKind { EK_Always, EK_Never, EK_ReplyHazy }; 168 169 virtual ExtKind hasExternalDefinitions(const Decl *D); 170 171 /// Finds all declarations lexically contained within the given 172 /// DeclContext, after applying an optional filter predicate. 173 /// 174 /// \param IsKindWeWant a predicate function that returns true if the passed 175 /// declaration kind is one we are looking for. 176 /// 177 /// The default implementation of this method is a no-op. 178 virtual void 179 FindExternalLexicalDecls(const DeclContext *DC, 180 llvm::function_ref<bool(Decl::Kind)> IsKindWeWant, 181 SmallVectorImpl<Decl *> &Result); 182 183 /// Finds all declarations lexically contained within the given 184 /// DeclContext. FindExternalLexicalDecls(const DeclContext * DC,SmallVectorImpl<Decl * > & Result)185 void FindExternalLexicalDecls(const DeclContext *DC, 186 SmallVectorImpl<Decl *> &Result) { 187 FindExternalLexicalDecls(DC, [](Decl::Kind) { return true; }, Result); 188 } 189 190 /// Get the decls that are contained in a file in the Offset/Length 191 /// range. \p Length can be 0 to indicate a point at \p Offset instead of 192 /// a range. 193 virtual void FindFileRegionDecls(FileID File, unsigned Offset, 194 unsigned Length, 195 SmallVectorImpl<Decl *> &Decls); 196 197 /// Gives the external AST source an opportunity to complete 198 /// the redeclaration chain for a declaration. Called each time we 199 /// need the most recent declaration of a declaration after the 200 /// generation count is incremented. 201 virtual void CompleteRedeclChain(const Decl *D); 202 203 /// Gives the external AST source an opportunity to complete 204 /// an incomplete type. 205 virtual void CompleteType(TagDecl *Tag); 206 207 /// Gives the external AST source an opportunity to complete an 208 /// incomplete Objective-C class. 209 /// 210 /// This routine will only be invoked if the "externally completed" bit is 211 /// set on the ObjCInterfaceDecl via the function 212 /// \c ObjCInterfaceDecl::setExternallyCompleted(). 213 virtual void CompleteType(ObjCInterfaceDecl *Class); 214 215 /// Loads comment ranges. 216 virtual void ReadComments(); 217 218 /// Notify ExternalASTSource that we started deserialization of 219 /// a decl or type so until FinishedDeserializing is called there may be 220 /// decls that are initializing. Must be paired with FinishedDeserializing. 221 /// 222 /// The default implementation of this method is a no-op. 223 virtual void StartedDeserializing(); 224 225 /// Notify ExternalASTSource that we finished the deserialization of 226 /// a decl or type. Must be paired with StartedDeserializing. 227 /// 228 /// The default implementation of this method is a no-op. 229 virtual void FinishedDeserializing(); 230 231 /// Function that will be invoked when we begin parsing a new 232 /// translation unit involving this external AST source. 233 /// 234 /// The default implementation of this method is a no-op. 235 virtual void StartTranslationUnit(ASTConsumer *Consumer); 236 237 /// Print any statistics that have been gathered regarding 238 /// the external AST source. 239 /// 240 /// The default implementation of this method is a no-op. 241 virtual void PrintStats(); 242 243 /// Perform layout on the given record. 244 /// 245 /// This routine allows the external AST source to provide an specific 246 /// layout for a record, overriding the layout that would normally be 247 /// constructed. It is intended for clients who receive specific layout 248 /// details rather than source code (such as LLDB). The client is expected 249 /// to fill in the field offsets, base offsets, virtual base offsets, and 250 /// complete object size. 251 /// 252 /// \param Record The record whose layout is being requested. 253 /// 254 /// \param Size The final size of the record, in bits. 255 /// 256 /// \param Alignment The final alignment of the record, in bits. 257 /// 258 /// \param FieldOffsets The offset of each of the fields within the record, 259 /// expressed in bits. All of the fields must be provided with offsets. 260 /// 261 /// \param BaseOffsets The offset of each of the direct, non-virtual base 262 /// classes. If any bases are not given offsets, the bases will be laid 263 /// out according to the ABI. 264 /// 265 /// \param VirtualBaseOffsets The offset of each of the virtual base classes 266 /// (either direct or not). If any bases are not given offsets, the bases will be laid 267 /// out according to the ABI. 268 /// 269 /// \returns true if the record layout was provided, false otherwise. 270 virtual bool layoutRecordType( 271 const RecordDecl *Record, uint64_t &Size, uint64_t &Alignment, 272 llvm::DenseMap<const FieldDecl *, uint64_t> &FieldOffsets, 273 llvm::DenseMap<const CXXRecordDecl *, CharUnits> &BaseOffsets, 274 llvm::DenseMap<const CXXRecordDecl *, CharUnits> &VirtualBaseOffsets); 275 276 //===--------------------------------------------------------------------===// 277 // Queries for performance analysis. 278 //===--------------------------------------------------------------------===// 279 280 struct MemoryBufferSizes { 281 size_t malloc_bytes; 282 size_t mmap_bytes; 283 MemoryBufferSizesMemoryBufferSizes284 MemoryBufferSizes(size_t malloc_bytes, size_t mmap_bytes) 285 : malloc_bytes(malloc_bytes), mmap_bytes(mmap_bytes) {} 286 }; 287 288 /// Return the amount of memory used by memory buffers, breaking down 289 /// by heap-backed versus mmap'ed memory. getMemoryBufferSizes()290 MemoryBufferSizes getMemoryBufferSizes() const { 291 MemoryBufferSizes sizes(0, 0); 292 getMemoryBufferSizes(sizes); 293 return sizes; 294 } 295 296 virtual void getMemoryBufferSizes(MemoryBufferSizes &sizes) const; 297 298 /// LLVM-style RTTI. 299 /// \{ isA(const void * ClassID)300 virtual bool isA(const void *ClassID) const { return ClassID == &ID; } classof(const ExternalASTSource * S)301 static bool classof(const ExternalASTSource *S) { return S->isA(&ID); } 302 /// \} 303 304 protected: 305 static DeclContextLookupResult 306 SetExternalVisibleDeclsForName(const DeclContext *DC, 307 DeclarationName Name, 308 ArrayRef<NamedDecl*> Decls); 309 310 static DeclContextLookupResult 311 SetNoExternalVisibleDeclsForName(const DeclContext *DC, 312 DeclarationName Name); 313 314 /// Increment the current generation. 315 uint32_t incrementGeneration(ASTContext &C); 316 }; 317 318 /// A lazy pointer to an AST node (of base type T) that resides 319 /// within an external AST source. 320 /// 321 /// The AST node is identified within the external AST source by a 322 /// 63-bit offset, and can be retrieved via an operation on the 323 /// external AST source itself. 324 template<typename T, typename OffsT, T* (ExternalASTSource::*Get)(OffsT Offset)> 325 struct LazyOffsetPtr { 326 /// Either a pointer to an AST node or the offset within the 327 /// external AST source where the AST node can be found. 328 /// 329 /// If the low bit is clear, a pointer to the AST node. If the low 330 /// bit is set, the upper 63 bits are the offset. 331 static constexpr size_t DataSize = std::max(sizeof(uint64_t), sizeof(T *)); 332 alignas(uint64_t) alignas(T *) mutable unsigned char Data[DataSize] = {}; 333 GetLSBLazyOffsetPtr334 unsigned char GetLSB() const { 335 return Data[llvm::sys::IsBigEndianHost ? DataSize - 1 : 0]; 336 } 337 AsLazyOffsetPtr338 template <typename U> U &As(bool New) const { 339 unsigned char *Obj = 340 Data + (llvm::sys::IsBigEndianHost ? DataSize - sizeof(U) : 0); 341 if (New) 342 return *new (Obj) U; 343 return *std::launder(reinterpret_cast<U *>(Obj)); 344 } 345 GetPtrLazyOffsetPtr346 T *&GetPtr() const { return As<T *>(false); } GetU64LazyOffsetPtr347 uint64_t &GetU64() const { return As<uint64_t>(false); } SetPtrLazyOffsetPtr348 void SetPtr(T *Ptr) const { As<T *>(true) = Ptr; } SetU64LazyOffsetPtr349 void SetU64(uint64_t U64) const { As<uint64_t>(true) = U64; } 350 351 public: 352 LazyOffsetPtr() = default; LazyOffsetPtrLazyOffsetPtr353 explicit LazyOffsetPtr(T *Ptr) : Data() { SetPtr(Ptr); } 354 LazyOffsetPtrLazyOffsetPtr355 explicit LazyOffsetPtr(uint64_t Offset) : Data() { 356 assert((Offset << 1 >> 1) == Offset && "Offsets must require < 63 bits"); 357 if (Offset == 0) 358 SetPtr(nullptr); 359 else 360 SetU64((Offset << 1) | 0x01); 361 } 362 363 LazyOffsetPtr &operator=(T *Ptr) { 364 SetPtr(Ptr); 365 return *this; 366 } 367 368 LazyOffsetPtr &operator=(uint64_t Offset) { 369 assert((Offset << 1 >> 1) == Offset && "Offsets must require < 63 bits"); 370 if (Offset == 0) 371 SetPtr(nullptr); 372 else 373 SetU64((Offset << 1) | 0x01); 374 375 return *this; 376 } 377 378 /// Whether this pointer is non-NULL. 379 /// 380 /// This operation does not require the AST node to be deserialized. 381 explicit operator bool() const { return isOffset() || GetPtr() != nullptr; } 382 383 /// Whether this pointer is non-NULL. 384 /// 385 /// This operation does not require the AST node to be deserialized. isValidLazyOffsetPtr386 bool isValid() const { return isOffset() || GetPtr() != nullptr; } 387 388 /// Whether this pointer is currently stored as an offset. isOffsetLazyOffsetPtr389 bool isOffset() const { return GetLSB() & 0x01; } 390 391 /// Retrieve the pointer to the AST node that this lazy pointer points to. 392 /// 393 /// \param Source the external AST source. 394 /// 395 /// \returns a pointer to the AST node. getLazyOffsetPtr396 T *get(ExternalASTSource *Source) const { 397 if (isOffset()) { 398 assert(Source && 399 "Cannot deserialize a lazy pointer without an AST source"); 400 SetPtr((Source->*Get)(OffsT(GetU64() >> 1))); 401 } 402 return GetPtr(); 403 } 404 405 /// Retrieve the address of the AST node pointer. Deserializes the pointee if 406 /// necessary. getAddressOfPointerLazyOffsetPtr407 T **getAddressOfPointer(ExternalASTSource *Source) const { 408 // Ensure the integer is in pointer form. 409 (void)get(Source); 410 return &GetPtr(); 411 } 412 }; 413 414 /// A lazy value (of type T) that is within an AST node of type Owner, 415 /// where the value might change in later generations of the external AST 416 /// source. 417 template<typename Owner, typename T, void (ExternalASTSource::*Update)(Owner)> 418 struct LazyGenerationalUpdatePtr { 419 /// A cache of the value of this pointer, in the most recent generation in 420 /// which we queried it. 421 struct LazyData { 422 ExternalASTSource *ExternalSource; 423 uint32_t LastGeneration = 0; 424 T LastValue; 425 LazyDataLazyGenerationalUpdatePtr::LazyData426 LazyData(ExternalASTSource *Source, T Value) 427 : ExternalSource(Source), LastValue(Value) {} 428 }; 429 430 // Our value is represented as simply T if there is no external AST source. 431 using ValueType = llvm::PointerUnion<T, LazyData*>; 432 ValueType Value; 433 LazyGenerationalUpdatePtrLazyGenerationalUpdatePtr434 LazyGenerationalUpdatePtr(ValueType V) : Value(V) {} 435 436 // Defined in ASTContext.h 437 static ValueType makeValue(const ASTContext &Ctx, T Value); 438 439 public: 440 explicit LazyGenerationalUpdatePtr(const ASTContext &Ctx, T Value = T()) ValueLazyGenerationalUpdatePtr441 : Value(makeValue(Ctx, Value)) {} 442 443 /// Create a pointer that is not potentially updated by later generations of 444 /// the external AST source. 445 enum NotUpdatedTag { NotUpdated }; 446 LazyGenerationalUpdatePtr(NotUpdatedTag, T Value = T()) ValueLazyGenerationalUpdatePtr447 : Value(Value) {} 448 449 /// Forcibly set this pointer (which must be lazy) as needing updates. markIncompleteLazyGenerationalUpdatePtr450 void markIncomplete() { 451 Value.template get<LazyData *>()->LastGeneration = 0; 452 } 453 454 /// Set the value of this pointer, in the current generation. setLazyGenerationalUpdatePtr455 void set(T NewValue) { 456 if (auto *LazyVal = Value.template dyn_cast<LazyData *>()) { 457 LazyVal->LastValue = NewValue; 458 return; 459 } 460 Value = NewValue; 461 } 462 463 /// Set the value of this pointer, for this and all future generations. setNotUpdatedLazyGenerationalUpdatePtr464 void setNotUpdated(T NewValue) { Value = NewValue; } 465 466 /// Get the value of this pointer, updating its owner if necessary. getLazyGenerationalUpdatePtr467 T get(Owner O) { 468 if (auto *LazyVal = Value.template dyn_cast<LazyData *>()) { 469 if (LazyVal->LastGeneration != LazyVal->ExternalSource->getGeneration()) { 470 LazyVal->LastGeneration = LazyVal->ExternalSource->getGeneration(); 471 (LazyVal->ExternalSource->*Update)(O); 472 } 473 return LazyVal->LastValue; 474 } 475 return Value.template get<T>(); 476 } 477 478 /// Get the most recently computed value of this pointer without updating it. getNotUpdatedLazyGenerationalUpdatePtr479 T getNotUpdated() const { 480 if (auto *LazyVal = Value.template dyn_cast<LazyData *>()) 481 return LazyVal->LastValue; 482 return Value.template get<T>(); 483 } 484 getOpaqueValueLazyGenerationalUpdatePtr485 void *getOpaqueValue() { return Value.getOpaqueValue(); } getFromOpaqueValueLazyGenerationalUpdatePtr486 static LazyGenerationalUpdatePtr getFromOpaqueValue(void *Ptr) { 487 return LazyGenerationalUpdatePtr(ValueType::getFromOpaqueValue(Ptr)); 488 } 489 }; 490 491 } // namespace clang 492 493 namespace llvm { 494 495 /// Specialize PointerLikeTypeTraits to allow LazyGenerationalUpdatePtr to be 496 /// placed into a PointerUnion. 497 template<typename Owner, typename T, 498 void (clang::ExternalASTSource::*Update)(Owner)> 499 struct PointerLikeTypeTraits< 500 clang::LazyGenerationalUpdatePtr<Owner, T, Update>> { 501 using Ptr = clang::LazyGenerationalUpdatePtr<Owner, T, Update>; 502 503 static void *getAsVoidPointer(Ptr P) { return P.getOpaqueValue(); } 504 static Ptr getFromVoidPointer(void *P) { return Ptr::getFromOpaqueValue(P); } 505 506 static constexpr int NumLowBitsAvailable = 507 PointerLikeTypeTraits<T>::NumLowBitsAvailable - 1; 508 }; 509 510 } // namespace llvm 511 512 namespace clang { 513 514 /// Represents a lazily-loaded vector of data. 515 /// 516 /// The lazily-loaded vector of data contains data that is partially loaded 517 /// from an external source and partially added by local translation. The 518 /// items loaded from the external source are loaded lazily, when needed for 519 /// iteration over the complete vector. 520 template<typename T, typename Source, 521 void (Source::*Loader)(SmallVectorImpl<T>&), 522 unsigned LoadedStorage = 2, unsigned LocalStorage = 4> 523 class LazyVector { 524 SmallVector<T, LoadedStorage> Loaded; 525 SmallVector<T, LocalStorage> Local; 526 527 public: 528 /// Iteration over the elements in the vector. 529 /// 530 /// In a complete iteration, the iterator walks the range [-M, N), 531 /// where negative values are used to indicate elements 532 /// loaded from the external source while non-negative values are used to 533 /// indicate elements added via \c push_back(). 534 /// However, to provide iteration in source order (for, e.g., chained 535 /// precompiled headers), dereferencing the iterator flips the negative 536 /// values (corresponding to loaded entities), so that position -M 537 /// corresponds to element 0 in the loaded entities vector, position -M+1 538 /// corresponds to element 1 in the loaded entities vector, etc. This 539 /// gives us a reasonably efficient, source-order walk. 540 /// 541 /// We define this as a wrapping iterator around an int. The 542 /// iterator_adaptor_base class forwards the iterator methods to basic integer 543 /// arithmetic. 544 class iterator 545 : public llvm::iterator_adaptor_base< 546 iterator, int, std::random_access_iterator_tag, T, int, T *, T &> { 547 friend class LazyVector; 548 549 LazyVector *Self; 550 551 iterator(LazyVector *Self, int Position) 552 : iterator::iterator_adaptor_base(Position), Self(Self) {} 553 554 bool isLoaded() const { return this->I < 0; } 555 556 public: 557 iterator() : iterator(nullptr, 0) {} 558 559 typename iterator::reference operator*() const { 560 if (isLoaded()) 561 return Self->Loaded.end()[this->I]; 562 return Self->Local.begin()[this->I]; 563 } 564 }; 565 566 iterator begin(Source *source, bool LocalOnly = false) { 567 if (LocalOnly) 568 return iterator(this, 0); 569 570 if (source) 571 (source->*Loader)(Loaded); 572 return iterator(this, -(int)Loaded.size()); 573 } 574 575 iterator end() { 576 return iterator(this, Local.size()); 577 } 578 579 void push_back(const T& LocalValue) { 580 Local.push_back(LocalValue); 581 } 582 583 void erase(iterator From, iterator To) { 584 if (From.isLoaded() && To.isLoaded()) { 585 Loaded.erase(&*From, &*To); 586 return; 587 } 588 589 if (From.isLoaded()) { 590 Loaded.erase(&*From, Loaded.end()); 591 From = begin(nullptr, true); 592 } 593 594 Local.erase(&*From, &*To); 595 } 596 }; 597 598 /// A lazy pointer to a statement. 599 using LazyDeclStmtPtr = 600 LazyOffsetPtr<Stmt, uint64_t, &ExternalASTSource::GetExternalDeclStmt>; 601 602 /// A lazy pointer to a declaration. 603 using LazyDeclPtr = 604 LazyOffsetPtr<Decl, GlobalDeclID, &ExternalASTSource::GetExternalDecl>; 605 606 /// A lazy pointer to a set of CXXCtorInitializers. 607 using LazyCXXCtorInitializersPtr = 608 LazyOffsetPtr<CXXCtorInitializer *, uint64_t, 609 &ExternalASTSource::GetExternalCXXCtorInitializers>; 610 611 /// A lazy pointer to a set of CXXBaseSpecifiers. 612 using LazyCXXBaseSpecifiersPtr = 613 LazyOffsetPtr<CXXBaseSpecifier, uint64_t, 614 &ExternalASTSource::GetExternalCXXBaseSpecifiers>; 615 616 } // namespace clang 617 618 #endif // LLVM_CLANG_AST_EXTERNALASTSOURCE_H 619