//===- 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