| //===- 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 <cassert> |
| |
| 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<Decls, 1, bool>; |
| |
| /// 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<typename Fn> |
| 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<DeclListNode*>()) { |
| NewTail = &Node->Rest; |
| List = Node->Rest; |
| } else { |
| break; |
| } |
| } else if (DeclListNode *N = List.dyn_cast<DeclListNode*>()) { |
| 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, <rest>); |
| // replace it with just D. |
| if (NewLast) { |
| DeclListNode *Node = NewLast->get<DeclListNode*>(); |
| *NewLast = Node->D; |
| C.DeallocateDeclListNode(Node); |
| } |
| break; |
| } |
| } |
| Data.setPointer(NewHead); |
| |
| assert(llvm::none_of(getLookupResult(), ShouldErase) && "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(false); |
| } |
| |
| 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<DeclListNode *>()) { |
| List = ToDealloc->Rest; |
| C.DeallocateDeclListNode(ToDealloc); |
| } |
| } |
| |
| ~StoredDeclsList() { |
| MaybeDeallocList(); |
| } |
| |
| StoredDeclsList &operator=(StoredDeclsList &&RHS) { |
| MaybeDeallocList(); |
| |
| Data = RHS.Data; |
| RHS.Data.setPointer(nullptr); |
| RHS.Data.setInt(false); |
| 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<NamedDecl *>(); |
| } |
| |
| DeclListNode *getAsList() const { |
| return getAsListAndHasExternal().getPointer().dyn_cast<DeclListNode*>(); |
| } |
| |
| bool hasExternalDecls() const { |
| return getAsListAndHasExternal().getInt(); |
| } |
| |
| void setHasExternalDecls() { |
| Data.setInt(true); |
| } |
| |
| 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(false); |
| } |
| |
| void replaceExternalDecls(ArrayRef<NamedDecl*> 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(false); |
| |
| 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<DeclListNode *>()) |
| Tail = &Node->Rest; |
| |
| // Append the Decls. |
| DeclListNode *Node = C.AllocateDeclListNode(Tail->get<NamedDecl *>()); |
| 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<DeclListNode *>()) { |
| if (D->declarationReplaces(N->D, IsKnownNewer)) { |
| N->D = D; |
| return; |
| } |
| if (auto *ND = N->Rest.dyn_cast<NamedDecl *>()) { |
| 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() << "<null>\n"; |
| return; |
| } |
| |
| while (true) { |
| if (auto *Node = D.dyn_cast<DeclListNode*>()) { |
| llvm::errs() << '[' << Node->D << "] -> "; |
| D = Node->Rest; |
| } else { |
| llvm::errs() << '[' << D.get<NamedDecl*>() << "]\n"; |
| return; |
| } |
| } |
| } |
| }; |
| |
| class StoredDeclsMap |
| : public llvm::SmallDenseMap<DeclarationName, StoredDeclsList, 4> { |
| friend class ASTContext; // walks the chain deleting these |
| friend class DeclContext; |
| |
| llvm::PointerIntPair<StoredDeclsMap*, 1> 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 |