//===- DeclContextInternals.h - DeclContext Representation ------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file defines the data structures used in the implementation // of DeclContext. // //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_AST_DECLCONTEXTINTERNALS_H #define LLVM_CLANG_AST_DECLCONTEXTINTERNALS_H #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclBase.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclarationName.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/PointerIntPair.h" #include "llvm/ADT/PointerUnion.h" #include namespace clang { class DependentDiagnostic; /// An array of decls optimized for the common case of only containing /// one entry. class StoredDeclsList { using Decls = DeclListNode::Decls; /// A collection of declarations, with a flag to indicate if we have /// further external declarations. using DeclsAndHasExternalTy = llvm::PointerIntPair; /// The stored data, which will be either a pointer to a NamedDecl, /// or a pointer to a list with a flag to indicate if there are further /// external declarations. DeclsAndHasExternalTy Data; template void erase_if(Fn ShouldErase) { Decls List = Data.getPointer(); if (!List) return; ASTContext &C = getASTContext(); DeclListNode::Decls NewHead = nullptr; DeclListNode::Decls *NewLast = nullptr; DeclListNode::Decls *NewTail = &NewHead; while (true) { if (!ShouldErase(*DeclListNode::iterator(List))) { NewLast = NewTail; *NewTail = List; if (auto *Node = List.dyn_cast()) { NewTail = &Node->Rest; List = Node->Rest; } else { break; } } else if (DeclListNode *N = List.dyn_cast()) { List = N->Rest; C.DeallocateDeclListNode(N); } else { // We're discarding the last declaration in the list. The last node we // want to keep (if any) will be of the form DeclListNode(D, ); // replace it with just D. if (NewLast) { DeclListNode *Node = NewLast->get(); *NewLast = Node->D; C.DeallocateDeclListNode(Node); } break; } } Data.setPointer(NewHead); assert(llvm::find_if(getLookupResult(), ShouldErase) == getLookupResult().end() && "Still exists!"); } void erase(NamedDecl *ND) { erase_if([ND](NamedDecl *D) { return D == ND; }); } public: StoredDeclsList() = default; StoredDeclsList(StoredDeclsList &&RHS) : Data(RHS.Data) { RHS.Data.setPointer(nullptr); RHS.Data.setInt(0); } void MaybeDeallocList() { if (isNull()) return; // If this is a list-form, free the list. ASTContext &C = getASTContext(); Decls List = Data.getPointer(); while (DeclListNode *ToDealloc = List.dyn_cast()) { List = ToDealloc->Rest; C.DeallocateDeclListNode(ToDealloc); } } ~StoredDeclsList() { MaybeDeallocList(); } StoredDeclsList &operator=(StoredDeclsList &&RHS) { MaybeDeallocList(); Data = RHS.Data; RHS.Data.setPointer(nullptr); RHS.Data.setInt(0); return *this; } bool isNull() const { return Data.getPointer().isNull(); } ASTContext &getASTContext() { assert(!isNull() && "No ASTContext."); if (NamedDecl *ND = getAsDecl()) return ND->getASTContext(); return getAsList()->D->getASTContext(); } DeclsAndHasExternalTy getAsListAndHasExternal() const { return Data; } NamedDecl *getAsDecl() const { return getAsListAndHasExternal().getPointer().dyn_cast(); } DeclListNode *getAsList() const { return getAsListAndHasExternal().getPointer().dyn_cast(); } bool hasExternalDecls() const { return getAsListAndHasExternal().getInt(); } void setHasExternalDecls() { Data.setInt(1); } void remove(NamedDecl *D) { assert(!isNull() && "removing from empty list"); erase(D); } /// Remove any declarations which were imported from an external AST source. void removeExternalDecls() { erase_if([](NamedDecl *ND) { return ND->isFromASTFile(); }); // Don't have any pending external decls any more. Data.setInt(0); } void replaceExternalDecls(ArrayRef Decls) { // Remove all declarations that are either external or are replaced with // external declarations. erase_if([Decls](NamedDecl *ND) { if (ND->isFromASTFile()) return true; for (NamedDecl *D : Decls) if (D->declarationReplaces(ND, /*IsKnownNewer=*/false)) return true; return false; }); // Don't have any pending external decls any more. Data.setInt(0); if (Decls.empty()) return; // Convert Decls into a list, in order. ASTContext &C = Decls.front()->getASTContext(); DeclListNode::Decls DeclsAsList = Decls.back(); for (size_t I = Decls.size() - 1; I != 0; --I) { DeclListNode *Node = C.AllocateDeclListNode(Decls[I - 1]); Node->Rest = DeclsAsList; DeclsAsList = Node; } DeclListNode::Decls Head = Data.getPointer(); if (Head.isNull()) { Data.setPointer(DeclsAsList); return; } // Find the end of the existing list. // FIXME: It would be possible to preserve information from erase_if to // avoid this rescan looking for the end of the list. DeclListNode::Decls *Tail = &Head; while (DeclListNode *Node = Tail->dyn_cast()) Tail = &Node->Rest; // Append the Decls. DeclListNode *Node = C.AllocateDeclListNode(Tail->get()); Node->Rest = DeclsAsList; *Tail = Node; Data.setPointer(Head); } /// Return an array of all the decls that this list represents. DeclContext::lookup_result getLookupResult() const { return DeclContext::lookup_result(Data.getPointer()); } /// If this is a redeclaration of an existing decl, replace the old one with /// D. Otherwise, append D. void addOrReplaceDecl(NamedDecl *D) { const bool IsKnownNewer = true; if (isNull()) { Data.setPointer(D); return; } // Most decls only have one entry in their list, special case it. if (NamedDecl *OldD = getAsDecl()) { if (D->declarationReplaces(OldD, IsKnownNewer)) { Data.setPointer(D); return; } // Add D after OldD. ASTContext &C = D->getASTContext(); DeclListNode *Node = C.AllocateDeclListNode(OldD); Node->Rest = D; Data.setPointer(Node); return; } // FIXME: Move the assert before the single decl case when we fix the // duplication coming from the ASTReader reading builtin types. assert(!llvm::is_contained(getLookupResult(), D) && "Already exists!"); // Determine if this declaration is actually a redeclaration. for (DeclListNode *N = getAsList(); /*return in loop*/; N = N->Rest.dyn_cast()) { if (D->declarationReplaces(N->D, IsKnownNewer)) { N->D = D; return; } if (auto *ND = N->Rest.dyn_cast()) { if (D->declarationReplaces(ND, IsKnownNewer)) { N->Rest = D; return; } // Add D after ND. ASTContext &C = D->getASTContext(); DeclListNode *Node = C.AllocateDeclListNode(ND); N->Rest = Node; Node->Rest = D; return; } } } /// Add a declaration to the list without checking if it replaces anything. void prependDeclNoReplace(NamedDecl *D) { if (isNull()) { Data.setPointer(D); return; } ASTContext &C = D->getASTContext(); DeclListNode *Node = C.AllocateDeclListNode(D); Node->Rest = Data.getPointer(); Data.setPointer(Node); } LLVM_DUMP_METHOD void dump() const { Decls D = Data.getPointer(); if (!D) { llvm::errs() << "\n"; return; } while (true) { if (auto *Node = D.dyn_cast()) { llvm::errs() << '[' << Node->D << "] -> "; D = Node->Rest; } else { llvm::errs() << '[' << D.get() << "]\n"; return; } } } }; class StoredDeclsMap : public llvm::SmallDenseMap { friend class ASTContext; // walks the chain deleting these friend class DeclContext; llvm::PointerIntPair Previous; public: static void DestroyAll(StoredDeclsMap *Map, bool Dependent); }; class DependentStoredDeclsMap : public StoredDeclsMap { friend class DeclContext; // iterates over diagnostics friend class DependentDiagnostic; DependentDiagnostic *FirstDiagnostic = nullptr; public: DependentStoredDeclsMap() = default; }; } // namespace clang #endif // LLVM_CLANG_AST_DECLCONTEXTINTERNALS_H