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