Make iteration over the DeclContext::lookup_result safe.

The idiom:
```
DeclContext::lookup_result R = DeclContext::lookup(Name);
for (auto *D : R) {...}
```

is not safe when in the loop body we trigger deserialization from an AST file.
The deserialization can insert new declarations in the StoredDeclsList whose
underlying type is a vector. When the vector decides to reallocate its storage
the pointer we hold becomes invalid.

This patch replaces a SmallVector with an singly-linked list. The current
approach stores a SmallVector<NamedDecl*, 4> which is around 8 pointers.
The linked list is 3, 5, or 7. We do better in terms of memory usage for small
cases (and worse in terms of locality -- the linked list entries won't be near
each other, but will be near their corresponding declarations, and we were going
to fetch those memory pages anyway). For larger cases: the vector uses a
doubling strategy for reallocation, so will generally be between half-full and
full. Let's say it's 75% full on average, so there's N * 4/3 + 4 pointers' worth
of space allocated currently and will be 2N pointers with the linked list. So we
break even when there are N=6 entries and slightly lose in terms of memory usage
after that. We suspect that's still a win on average.

Thanks to @rsmith!

Differential revision: https://reviews.llvm.org/D91524
This commit is contained in:
Vassil Vassilev 2021-03-17 08:56:05 +00:00
parent 3b8b5d1f22
commit 0cb7e7ca0c
30 changed files with 477 additions and 403 deletions

View File

@ -195,7 +195,7 @@ const NamedDecl &findDecl(ParsedAST &AST, llvm::StringRef QName) {
llvm::StringRef Name) -> const NamedDecl & {
auto LookupRes = Scope.lookup(DeclarationName(&Ctx.Idents.get(Name)));
assert(!LookupRes.empty() && "Lookup failed");
assert(LookupRes.size() == 1 && "Lookup returned multiple results");
assert(LookupRes.isSingleResult() && "Lookup returned multiple results");
return *LookupRes.front();
};

View File

@ -604,6 +604,9 @@ private:
std::unique_ptr<interp::Context> InterpContext;
std::unique_ptr<ParentMapContext> ParentMapCtx;
/// Keeps track of the deallocated DeclListNodes for future reuse.
DeclListNode *ListNodeFreeList = nullptr;
public:
IdentifierTable &Idents;
SelectorTable &Selectors;
@ -655,6 +658,24 @@ public:
}
void Deallocate(void *Ptr) const {}
/// Allocates a \c DeclListNode or returns one from the \c ListNodeFreeList
/// pool.
DeclListNode *AllocateDeclListNode(clang::NamedDecl *ND) {
if (DeclListNode *Alloc = ListNodeFreeList) {
ListNodeFreeList = Alloc->Rest.dyn_cast<DeclListNode*>();
Alloc->D = ND;
Alloc->Rest = nullptr;
return Alloc;
}
return new (*this) DeclListNode(ND);
}
/// Deallcates a \c DeclListNode by returning it to the \c ListNodeFreeList
/// pool.
void DeallocateDeclListNode(DeclListNode *N) {
N->Rest = ListNodeFreeList;
ListNodeFreeList = N;
}
/// Return the total amount of physical memory allocated for representing
/// AST nodes and type information.
size_t getASTAllocatedMemory() const {

View File

@ -76,9 +76,8 @@ public:
CXXBasePath() = default;
/// The set of declarations found inside this base class
/// subobject.
DeclContext::lookup_result Decls;
/// The declarations found inside this base class subobject.
DeclContext::lookup_iterator Decls;
void clear() {
SmallVectorImpl<CXXBasePathElement>::clear();

View File

@ -579,6 +579,16 @@ public:
AnonOrFirstNamespaceAndInline.setInt(Inline);
}
/// Returns true if the inline qualifier for \c Name is redundant.
bool isRedundantInlineQualifierFor(DeclarationName Name) const {
if (!isInline())
return false;
auto X = lookup(Name);
auto Y = getParent()->lookup(Name);
return std::distance(X.begin(), X.end()) ==
std::distance(Y.begin(), Y.end());
}
/// Get the original (first) namespace declaration.
NamespaceDecl *getOriginalNamespace();

View File

@ -1220,65 +1220,110 @@ public:
void print(raw_ostream &OS) const override;
};
} // namespace clang
/// The results of name lookup within a DeclContext. This is either a
/// single result (with no stable storage) or a collection of results (with
/// stable storage provided by the lookup table).
// Required to determine the layout of the PointerUnion<NamedDecl*> before
// seeing the NamedDecl definition being first used in DeclListNode::operator*.
namespace llvm {
template <> struct PointerLikeTypeTraits<::clang::NamedDecl *> {
static inline void *getAsVoidPointer(::clang::NamedDecl *P) { return P; }
static inline ::clang::NamedDecl *getFromVoidPointer(void *P) {
return static_cast<::clang::NamedDecl *>(P);
}
static constexpr int NumLowBitsAvailable = 3;
};
}
namespace clang {
/// A list storing NamedDecls in the lookup tables.
class DeclListNode {
friend class ASTContext; // allocate, deallocate nodes.
friend class StoredDeclsList;
public:
using Decls = llvm::PointerUnion<NamedDecl*, DeclListNode*>;
class iterator {
friend class DeclContextLookupResult;
friend class StoredDeclsList;
Decls Ptr;
iterator(Decls Node) : Ptr(Node) { }
public:
using difference_type = ptrdiff_t;
using value_type = NamedDecl*;
using pointer = void;
using reference = value_type;
using iterator_category = std::forward_iterator_tag;
iterator() = default;
reference operator*() const {
assert(Ptr && "dereferencing end() iterator");
if (DeclListNode *CurNode = Ptr.dyn_cast<DeclListNode*>())
return CurNode->D;
return Ptr.get<NamedDecl*>();
}
void operator->() const { } // Unsupported.
bool operator==(const iterator &X) const { return Ptr == X.Ptr; }
bool operator!=(const iterator &X) const { return Ptr != X.Ptr; }
inline iterator &operator++() { // ++It
assert(!Ptr.isNull() && "Advancing empty iterator");
if (DeclListNode *CurNode = Ptr.dyn_cast<DeclListNode*>())
Ptr = CurNode->Rest;
else
Ptr = nullptr;
return *this;
}
iterator operator++(int) { // It++
iterator temp = *this;
++(*this);
return temp;
}
// Enables the pattern for (iterator I =..., E = I.end(); I != E; ++I)
iterator end() { return iterator(); }
};
private:
NamedDecl *D = nullptr;
Decls Rest = nullptr;
DeclListNode(NamedDecl *ND) : D(ND) {}
};
/// The results of name lookup within a DeclContext.
class DeclContextLookupResult {
using ResultTy = ArrayRef<NamedDecl *>;
using Decls = DeclListNode::Decls;
ResultTy Result;
// If there is only one lookup result, it would be invalidated by
// reallocations of the name table, so store it separately.
NamedDecl *Single = nullptr;
static NamedDecl *const SingleElementDummyList;
/// When in collection form, this is what the Data pointer points to.
Decls Result;
public:
DeclContextLookupResult() = default;
DeclContextLookupResult(ArrayRef<NamedDecl *> Result)
: Result(Result) {}
DeclContextLookupResult(NamedDecl *Single)
: Result(SingleElementDummyList), Single(Single) {}
class iterator;
using IteratorBase =
llvm::iterator_adaptor_base<iterator, ResultTy::iterator,
std::random_access_iterator_tag, NamedDecl *>;
class iterator : public IteratorBase {
value_type SingleElement;
public:
explicit iterator(pointer Pos, value_type Single = nullptr)
: IteratorBase(Pos), SingleElement(Single) {}
reference operator*() const {
return SingleElement ? SingleElement : IteratorBase::operator*();
}
};
DeclContextLookupResult(Decls Result) : Result(Result) {}
using iterator = DeclListNode::iterator;
using const_iterator = iterator;
using pointer = iterator::pointer;
using reference = iterator::reference;
iterator begin() const { return iterator(Result.begin(), Single); }
iterator end() const { return iterator(Result.end(), Single); }
iterator begin() { return iterator(Result); }
iterator end() { return iterator(); }
const_iterator begin() const {
return const_cast<DeclContextLookupResult*>(this)->begin();
}
const_iterator end() const { return iterator(); }
bool empty() const { return Result.empty(); }
pointer data() const { return Single ? &Single : Result.data(); }
size_t size() const { return Single ? 1 : Result.size(); }
reference front() const { return Single ? Single : Result.front(); }
reference back() const { return Single ? Single : Result.back(); }
reference operator[](size_t N) const { return Single ? Single : Result[N]; }
bool empty() const { return Result.isNull(); }
bool isSingleResult() const { return Result.dyn_cast<NamedDecl*>(); }
reference front() const { return *begin(); }
// FIXME: Remove this from the interface
DeclContextLookupResult slice(size_t N) const {
DeclContextLookupResult Sliced = Result.slice(N);
Sliced.Single = Single;
return Sliced;
// Find the first declaration of the given type in the list. Note that this
// is not in general the earliest-declared declaration, and should only be
// used when it's not possible for there to be more than one match or where
// it doesn't matter which one is found.
template<class T> T *find_first() const {
for (auto *D : *this)
if (T *Decl = dyn_cast<T>(D))
return Decl;
return nullptr;
}
};

View File

@ -14,6 +14,7 @@
#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"
@ -21,7 +22,6 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/PointerUnion.h"
#include "llvm/ADT/SmallVector.h"
#include <algorithm>
#include <cassert>
@ -31,231 +31,287 @@ class DependentDiagnostic;
/// An array of decls optimized for the common case of only containing
/// one entry.
struct StoredDeclsList {
/// When in vector form, this is what the Data pointer points to.
using DeclsTy = SmallVector<NamedDecl *, 4>;
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<DeclsTy *, 1, bool>;
using DeclsAndHasExternalTy = llvm::PointerIntPair<Decls, 1, bool>;
/// The stored data, which will be either a pointer to a NamedDecl,
/// or a pointer to a vector with a flag to indicate if there are further
/// or a pointer to a list with a flag to indicate if there are further
/// external declarations.
llvm::PointerUnion<NamedDecl *, DeclsAndHasExternalTy> Data;
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::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 = (NamedDecl *)nullptr;
RHS.Data.setPointer(nullptr);
RHS.Data.setInt(0);
}
~StoredDeclsList() {
// If this is a vector-form, free the vector.
if (DeclsTy *Vector = getAsVector())
delete Vector;
}
StoredDeclsList &operator=(StoredDeclsList &&RHS) {
if (DeclsTy *Vector = getAsVector())
delete Vector;
Data = RHS.Data;
RHS.Data = (NamedDecl *)nullptr;
return *this;
}
bool isNull() const { return Data.isNull(); }
NamedDecl *getAsDecl() const {
return Data.dyn_cast<NamedDecl *>();
}
DeclsAndHasExternalTy getAsVectorAndHasExternal() const {
return Data.dyn_cast<DeclsAndHasExternalTy>();
}
DeclsTy *getAsVector() const {
return getAsVectorAndHasExternal().getPointer();
}
bool hasExternalDecls() const {
return getAsVectorAndHasExternal().getInt();
}
void setHasExternalDecls() {
if (DeclsTy *Vec = getAsVector())
Data = DeclsAndHasExternalTy(Vec, true);
else {
DeclsTy *VT = new DeclsTy();
if (NamedDecl *OldD = getAsDecl())
VT->push_back(OldD);
Data = DeclsAndHasExternalTy(VT, true);
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);
}
}
void setOnlyValue(NamedDecl *ND) {
assert(!getAsVector() && "Not inline");
Data = ND;
// Make sure that Data is a plain NamedDecl* so we can use its address
// at getLookupResult.
assert(*(NamedDecl **)&Data == ND &&
"PointerUnion mangles the NamedDecl pointer!");
~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<NamedDecl *>();
}
DeclListNode *getAsList() const {
return getAsListAndHasExternal().getPointer().dyn_cast<DeclListNode*>();
}
bool hasExternalDecls() const {
return getAsListAndHasExternal().getInt();
}
void setHasExternalDecls() {
Data.setInt(1);
}
void remove(NamedDecl *D) {
assert(!isNull() && "removing from empty list");
if (NamedDecl *Singleton = getAsDecl()) {
assert(Singleton == D && "list is different singleton");
(void)Singleton;
Data = (NamedDecl *)nullptr;
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<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(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;
}
DeclsTy &Vec = *getAsVector();
DeclsTy::iterator I = llvm::find(Vec, D);
assert(I != Vec.end() && "list does not contain decl");
Vec.erase(I);
// 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;
assert(llvm::find(Vec, D) == Vec.end() && "list still contains decl");
// Append the Decls.
DeclListNode *Node = C.AllocateDeclListNode(Tail->get<NamedDecl *>());
Node->Rest = DeclsAsList;
*Tail = Node;
Data.setPointer(Head);
}
/// Remove any declarations which were imported from an external
/// AST source.
void removeExternalDecls() {
/// 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()) {
// Nothing to do.
} else if (NamedDecl *Singleton = getAsDecl()) {
if (Singleton->isFromASTFile())
*this = StoredDeclsList();
} else {
DeclsTy &Vec = *getAsVector();
Vec.erase(std::remove_if(Vec.begin(), Vec.end(),
[](Decl *D) { return D->isFromASTFile(); }),
Vec.end());
// Don't have any external decls any more.
Data = DeclsAndHasExternalTy(&Vec, false);
}
}
/// getLookupResult - Return an array of all the decls that this list
/// represents.
DeclContext::lookup_result getLookupResult() {
if (isNull())
return DeclContext::lookup_result();
// If we have a single NamedDecl, return it.
if (NamedDecl *ND = getAsDecl()) {
assert(!isNull() && "Empty list isn't allowed");
// Data is a raw pointer to a NamedDecl*, return it.
return DeclContext::lookup_result(ND);
Data.setPointer(D);
return;
}
assert(getAsVector() && "Must have a vector at this point");
DeclsTy &Vector = *getAsVector();
// Otherwise, we have a range result.
return DeclContext::lookup_result(Vector);
}
/// HandleRedeclaration - If this is a redeclaration of an existing decl,
/// replace the old one with D and return true. Otherwise return false.
bool HandleRedeclaration(NamedDecl *D, bool IsKnownNewer) {
// Most decls only have one entry in their list, special case it.
if (NamedDecl *OldD = getAsDecl()) {
if (!D->declarationReplaces(OldD, IsKnownNewer))
return false;
setOnlyValue(D);
return true;
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.
DeclsTy &Vec = *getAsVector();
for (DeclsTy::iterator OD = Vec.begin(), ODEnd = Vec.end();
OD != ODEnd; ++OD) {
NamedDecl *OldD = *OD;
if (D->declarationReplaces(OldD, IsKnownNewer)) {
*OD = D;
return true;
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;
}
}
return false;
}
/// AddSubsequentDecl - This is called on the second and later decl when it is
/// not a redeclaration to merge it into the appropriate place in our list.
void AddSubsequentDecl(NamedDecl *D) {
assert(!isNull() && "don't AddSubsequentDecl when we have no decls");
// If this is the second decl added to the list, convert this to vector
// form.
if (NamedDecl *OldD = getAsDecl()) {
DeclsTy *VT = new DeclsTy();
VT->push_back(OldD);
Data = DeclsAndHasExternalTy(VT, false);
/// Add a declaration to the list without checking if it replaces anything.
void prependDeclNoReplace(NamedDecl *D) {
if (isNull()) {
Data.setPointer(D);
return;
}
DeclsTy &Vec = *getAsVector();
ASTContext &C = D->getASTContext();
DeclListNode *Node = C.AllocateDeclListNode(D);
Node->Rest = Data.getPointer();
Data.setPointer(Node);
}
// Using directives end up in a special entry which contains only
// other using directives, so all this logic is wasted for them.
// But avoiding the logic wastes time in the far-more-common case
// that we're *not* adding a new using directive.
LLVM_DUMP_METHOD void dump() const {
Decls D = Data.getPointer();
if (!D) {
llvm::errs() << "<null>\n";
return;
}
// Tag declarations always go at the end of the list so that an
// iterator which points at the first tag will start a span of
// decls that only contains tags.
if (D->hasTagIdentifierNamespace())
Vec.push_back(D);
// Resolved using declarations go at the front of the list so that
// they won't show up in other lookup results. Unresolved using
// declarations (which are always in IDNS_Using | IDNS_Ordinary)
// follow that so that the using declarations will be contiguous.
else if (D->getIdentifierNamespace() & Decl::IDNS_Using) {
DeclsTy::iterator I = Vec.begin();
if (D->getIdentifierNamespace() != Decl::IDNS_Using) {
while (I != Vec.end() &&
(*I)->getIdentifierNamespace() == Decl::IDNS_Using)
++I;
while (true) {
if (auto *Node = D.dyn_cast<DeclListNode*>()) {
llvm::errs() << '[' << Node->D << "] -> ";
D = Node->Rest;
} else {
llvm::errs() << '[' << D.get<NamedDecl*>() << "]\n";
return;
}
Vec.insert(I, D);
// All other declarations go at the end of the list, but before any
// tag declarations. But we can be clever about tag declarations
// because there can only ever be one in a scope.
} else if (!Vec.empty() && Vec.back()->hasTagIdentifierNamespace()) {
NamedDecl *TagD = Vec.back();
Vec.back() = D;
Vec.push_back(TagD);
} else
Vec.push_back(D);
}
}
};
class StoredDeclsMap
: public llvm::SmallDenseMap<DeclarationName, StoredDeclsList, 4> {
public:
static void DestroyAll(StoredDeclsMap *Map, bool Dependent);
private:
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 {
public:
DependentStoredDeclsMap() = default;
private:
friend class DeclContext; // iterates over diagnostics
friend class DependentDiagnostic;
DependentDiagnostic *FirstDiagnostic = nullptr;
public:
DependentStoredDeclsMap() = default;
};
} // namespace clang

View File

@ -84,7 +84,7 @@ class RecordDecl;
class Sema;
class SourceManager;
class Stmt;
struct StoredDeclsList;
class StoredDeclsList;
class SwitchCase;
class TemplateParameterList;
class Token;

View File

@ -613,7 +613,7 @@ ClassImplementsAllMethodsAndProperties(ASTContext &Ctx,
continue;
HasAtleastOneRequiredProperty = true;
DeclContext::lookup_result R = IDecl->lookup(Property->getDeclName());
if (R.size() == 0) {
if (R.empty()) {
// Relax the rule and look into class's implementation for a synthesize
// or dynamic declaration. Class is implementing a property coming from
// another protocol. This still makes the target protocol as conforming.
@ -621,14 +621,12 @@ ClassImplementsAllMethodsAndProperties(ASTContext &Ctx,
Property->getDeclName().getAsIdentifierInfo(),
Property->getQueryKind()))
return false;
}
else if (ObjCPropertyDecl *ClassProperty = dyn_cast<ObjCPropertyDecl>(R[0])) {
if ((ClassProperty->getPropertyAttributes()
!= Property->getPropertyAttributes()) ||
!Ctx.hasSameType(ClassProperty->getType(), Property->getType()))
return false;
}
else
} else if (auto *ClassProperty = R.find_first<ObjCPropertyDecl>()) {
if ((ClassProperty->getPropertyAttributes() !=
Property->getPropertyAttributes()) ||
!Ctx.hasSameType(ClassProperty->getType(), Property->getType()))
return false;
} else
return false;
}
@ -645,12 +643,12 @@ ClassImplementsAllMethodsAndProperties(ASTContext &Ctx,
if (MD->getImplementationControl() == ObjCMethodDecl::Optional)
continue;
DeclContext::lookup_result R = ImpDecl->lookup(MD->getDeclName());
if (R.size() == 0)
if (R.empty())
return false;
bool match = false;
HasAtleastOneRequiredMethod = true;
for (unsigned I = 0, N = R.size(); I != N; ++I)
if (ObjCMethodDecl *ImpMD = dyn_cast<ObjCMethodDecl>(R[0]))
for (NamedDecl *ND : R)
if (ObjCMethodDecl *ImpMD = dyn_cast<ObjCMethodDecl>(ND))
if (Ctx.ObjCMethodsAreEqual(MD, ImpMD)) {
match = true;
break;

View File

@ -10892,6 +10892,9 @@ void ASTContext::forEachMultiversionedFunctionVersion(
assert(FD->isMultiVersion() && "Only valid for multiversioned functions");
llvm::SmallDenseSet<const FunctionDecl*, 4> SeenDecls;
FD = FD->getMostRecentDecl();
// FIXME: The order of traversal here matters and depends on the order of
// lookup results, which happens to be (mostly) oldest-to-newest, but we
// shouldn't rely on that.
for (auto *CurDecl :
FD->getDeclContext()->getRedeclContext()->lookup(FD->getDeclName())) {
FunctionDecl *CurFD = CurDecl->getAsFunction()->getMostRecentDecl();

View File

@ -386,9 +386,9 @@ static bool isOrdinaryMember(const NamedDecl *ND) {
static bool findOrdinaryMember(const CXXRecordDecl *RD, CXXBasePath &Path,
DeclarationName Name) {
Path.Decls = RD->lookup(Name);
for (NamedDecl *ND : Path.Decls)
if (isOrdinaryMember(ND))
Path.Decls = RD->lookup(Name).begin();
for (DeclContext::lookup_iterator I = Path.Decls, E = I.end(); I != E; ++I)
if (isOrdinaryMember(*I))
return true;
return false;
@ -453,9 +453,10 @@ std::vector<const NamedDecl *> CXXRecordDecl::lookupDependentName(
},
Paths, /*LookupInDependent=*/true))
return Results;
for (const NamedDecl *ND : Paths.front().Decls) {
if (isOrdinaryMember(ND) && Filter(ND))
Results.push_back(ND);
for (DeclContext::lookup_iterator I = Paths.front().Decls, E = I.end();
I != E; ++I) {
if (isOrdinaryMember(*I) && Filter(*I))
Results.push_back(*I);
}
return Results;
}

View File

@ -1612,8 +1612,7 @@ void NamedDecl::printNestedNameSpecifier(raw_ostream &OS,
// Suppress inline namespace if it doesn't make the result ambiguous.
if (P.SuppressInlineNamespace && Ctx->isInlineNamespace() && NameInScope &&
Ctx->lookup(NameInScope).size() ==
Ctx->getParent()->lookup(NameInScope).size())
cast<NamespaceDecl>(Ctx)->isRedundantInlineQualifierFor(NameInScope))
continue;
// Skip non-named contexts such as linkage specifications and ExportDecls.

View File

@ -1394,39 +1394,7 @@ ExternalASTSource::SetExternalVisibleDeclsForName(const DeclContext *DC,
DC->reconcileExternalVisibleStorage();
StoredDeclsList &List = (*Map)[Name];
// Clear out any old external visible declarations, to avoid quadratic
// performance in the redeclaration checks below.
List.removeExternalDecls();
if (!List.isNull()) {
// We have both existing declarations and new declarations for this name.
// Some of the declarations may simply replace existing ones. Handle those
// first.
llvm::SmallVector<unsigned, 8> Skip;
for (unsigned I = 0, N = Decls.size(); I != N; ++I)
if (List.HandleRedeclaration(Decls[I], /*IsKnownNewer*/false))
Skip.push_back(I);
Skip.push_back(Decls.size());
// Add in any new declarations.
unsigned SkipPos = 0;
for (unsigned I = 0, N = Decls.size(); I != N; ++I) {
if (I == Skip[SkipPos])
++SkipPos;
else
List.AddSubsequentDecl(Decls[I]);
}
} else {
// Convert the array to a StoredDeclsList.
for (auto *D : Decls) {
if (List.isNull())
List.setOnlyValue(D);
else
List.AddSubsequentDecl(D);
}
}
List.replaceExternalDecls(Decls);
return List.getLookupResult();
}
@ -1538,10 +1506,7 @@ void DeclContext::removeDecl(Decl *D) {
if (Map) {
StoredDeclsMap::iterator Pos = Map->find(ND->getDeclName());
assert(Pos != Map->end() && "no lookup entry for decl");
// Remove the decl only if it is contained.
StoredDeclsList::DeclsTy *Vec = Pos->second.getAsVector();
if ((Vec && is_contained(*Vec, ND)) || Pos->second.getAsDecl() == ND)
Pos->second.remove(ND);
Pos->second.remove(ND);
}
} while (DC->isTransparentContext() && (DC = DC->getParent()));
}
@ -1658,8 +1623,6 @@ void DeclContext::buildLookupImpl(DeclContext *DCtx, bool Internal) {
}
}
NamedDecl *const DeclContextLookupResult::SingleElementDummyList = nullptr;
DeclContext::lookup_result
DeclContext::lookup(DeclarationName Name) const {
assert(getDeclKind() != Decl::LinkageSpec &&
@ -1935,23 +1898,11 @@ void DeclContext::makeDeclVisibleInContextImpl(NamedDecl *D, bool Internal) {
// In this case, we never try to replace an existing declaration; we'll
// handle that when we finalize the list of declarations for this name.
DeclNameEntries.setHasExternalDecls();
DeclNameEntries.AddSubsequentDecl(D);
DeclNameEntries.prependDeclNoReplace(D);
return;
}
if (DeclNameEntries.isNull()) {
DeclNameEntries.setOnlyValue(D);
return;
}
if (DeclNameEntries.HandleRedeclaration(D, /*IsKnownNewer*/!Internal)) {
// This declaration has replaced an existing one for which
// declarationReplaces returns true.
return;
}
// Put this declaration into the appropriate slot.
DeclNameEntries.AddSubsequentDecl(D);
DeclNameEntries.addOrReplaceDecl(D);
}
UsingDirectiveDecl *DeclContext::udir_iterator::operator*() const {

View File

@ -64,24 +64,24 @@ LookupSameContext(Source<TranslationUnitDecl *> SourceTU, const DeclContext *DC,
Source<DeclarationName> SourceName = *SourceNameOrErr;
DeclContext::lookup_result SearchResult =
SourceParentDC.get()->lookup(SourceName.get());
size_t SearchResultSize = SearchResult.size();
if (SearchResultSize == 0 || SearchResultSize > 1) {
// There are two cases here. First, we might not find the name.
// We might also find multiple copies, in which case we have no
// guarantee that the one we wanted is the one we pick. (E.g.,
// if we have two specializations of the same template it is
// very hard to determine which is the one you want.)
//
// The Origins map fixes this problem by allowing the origin to be
// explicitly recorded, so we trigger that recording by returning
// nothing (rather than a possibly-inaccurate guess) here.
return nullptr;
} else {
NamedDecl *SearchResultDecl = SearchResult[0];
// There are two cases here. First, we might not find the name.
// We might also find multiple copies, in which case we have no
// guarantee that the one we wanted is the one we pick. (E.g.,
// if we have two specializations of the same template it is
// very hard to determine which is the one you want.)
//
// The Origins map fixes this problem by allowing the origin to be
// explicitly recorded, so we trigger that recording by returning
// nothing (rather than a possibly-inaccurate guess) here.
if (SearchResult.isSingleResult()) {
NamedDecl *SearchResultDecl = SearchResult.front();
if (isa<DeclContext>(SearchResultDecl) &&
SearchResultDecl->getKind() == DC->getDeclKind())
return cast<DeclContext>(SearchResultDecl)->getPrimaryContext();
return nullptr; // This type of lookup is unsupported
} else {
return nullptr;
}
}

View File

@ -1235,8 +1235,7 @@ void TypePrinter::AppendScope(DeclContext *DC, raw_ostream &OS,
// Only suppress an inline namespace if the name has the same lookup
// results in the enclosing namespace.
if (Policy.SuppressInlineNamespace && NS->isInline() && NameInScope &&
DC->getParent()->lookup(NameInScope).size() ==
DC->lookup(NameInScope).size())
NS->isRedundantInlineQualifierFor(NameInScope))
return AppendScope(DC->getParent(), OS, NameInScope);
AppendScope(DC->getParent(), OS, NS->getDeclName());

View File

@ -4673,7 +4673,6 @@ public:
struct MultiVersionResolverOption {
llvm::Function *Function;
FunctionDecl *FD;
struct Conds {
StringRef Architecture;
llvm::SmallVector<StringRef, 8> Features;

View File

@ -10,7 +10,6 @@
//
//===----------------------------------------------------------------------===//
#include "clang/Sema/MultiplexExternalSemaSource.h"
#include "clang/AST/DeclContextInternals.h"
#include "clang/Sema/Lookup.h"
using namespace clang;

View File

@ -4130,13 +4130,9 @@ ValueDecl *Sema::tryLookupCtorInitMemberDecl(CXXRecordDecl *ClassDecl,
IdentifierInfo *MemberOrBase) {
if (SS.getScopeRep() || TemplateTypeTy)
return nullptr;
DeclContext::lookup_result Result = ClassDecl->lookup(MemberOrBase);
if (Result.empty())
return nullptr;
ValueDecl *Member;
if ((Member = dyn_cast<FieldDecl>(Result.front())) ||
(Member = dyn_cast<IndirectFieldDecl>(Result.front())))
return Member;
for (auto *D : ClassDecl->lookup(MemberOrBase))
if (isa<FieldDecl>(D) || isa<IndirectFieldDecl>(D))
return cast<ValueDecl>(D);
return nullptr;
}
@ -9672,9 +9668,9 @@ public:
bool foundSameNameMethod = false;
SmallVector<CXXMethodDecl *, 8> overloadedMethods;
for (Path.Decls = BaseRecord->lookup(Name); !Path.Decls.empty();
Path.Decls = Path.Decls.slice(1)) {
NamedDecl *D = Path.Decls.front();
for (Path.Decls = BaseRecord->lookup(Name).begin();
Path.Decls != DeclContext::lookup_iterator(); ++Path.Decls) {
NamedDecl *D = *Path.Decls;
if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(D)) {
MD = MD->getCanonicalDecl();
foundSameNameMethod = true;

View File

@ -638,8 +638,8 @@ void LookupResult::resolveKind() {
void LookupResult::addDeclsFromBasePaths(const CXXBasePaths &P) {
CXXBasePaths::const_paths_iterator I, E;
for (I = P.begin(), E = P.end(); I != E; ++I)
for (DeclContext::lookup_iterator DI = I->Decls.begin(),
DE = I->Decls.end(); DI != DE; ++DI)
for (DeclContext::lookup_iterator DI = I->Decls, DE = DI.end(); DI != DE;
++DI)
addDecl(*DI);
}
@ -2230,9 +2230,9 @@ bool Sema::LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx,
CXXRecordDecl *BaseRecord = Specifier->getType()->getAsCXXRecordDecl();
// Drop leading non-matching lookup results from the declaration list so
// we don't need to consider them again below.
for (Path.Decls = BaseRecord->lookup(Name); !Path.Decls.empty();
Path.Decls = Path.Decls.slice(1)) {
if (Path.Decls.front()->isInIdentifierNamespace(IDNS))
for (Path.Decls = BaseRecord->lookup(Name).begin();
Path.Decls != Path.Decls.end(); ++Path.Decls) {
if ((*Path.Decls)->isInIdentifierNamespace(IDNS))
return true;
}
return false;
@ -2256,9 +2256,9 @@ bool Sema::LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx,
AccessSpecifier SubobjectAccess = AS_none;
// Check whether the given lookup result contains only static members.
auto HasOnlyStaticMembers = [&](DeclContextLookupResult Result) {
for (NamedDecl *ND : Result)
if (ND->isInIdentifierNamespace(IDNS) && ND->isCXXInstanceMember())
auto HasOnlyStaticMembers = [&](DeclContext::lookup_iterator Result) {
for (DeclContext::lookup_iterator I = Result, E = I.end(); I != E; ++I)
if ((*I)->isInIdentifierNamespace(IDNS) && (*I)->isCXXInstanceMember())
return false;
return true;
};
@ -2267,8 +2267,8 @@ bool Sema::LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx,
// Determine whether two sets of members contain the same members, as
// required by C++ [class.member.lookup]p6.
auto HasSameDeclarations = [&](DeclContextLookupResult A,
DeclContextLookupResult B) {
auto HasSameDeclarations = [&](DeclContext::lookup_iterator A,
DeclContext::lookup_iterator B) {
using Iterator = DeclContextLookupResult::iterator;
using Result = const void *;
@ -2305,7 +2305,7 @@ bool Sema::LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx,
// We'll often find the declarations are in the same order. Handle this
// case (and the special case of only one declaration) efficiently.
Iterator AIt = A.begin(), BIt = B.begin(), AEnd = A.end(), BEnd = B.end();
Iterator AIt = A, BIt = B, AEnd, BEnd;
while (true) {
Result AResult = Next(AIt, AEnd);
Result BResult = Next(BIt, BEnd);
@ -2388,10 +2388,11 @@ bool Sema::LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx,
// Lookup in a base class succeeded; return these results.
for (auto *D : Paths.front().Decls) {
for (DeclContext::lookup_iterator I = Paths.front().Decls, E = I.end();
I != E; ++I) {
AccessSpecifier AS = CXXRecordDecl::MergeAccess(SubobjectAccess,
D->getAccess());
if (NamedDecl *ND = R.getAcceptableDecl(D))
(*I)->getAccess());
if (NamedDecl *ND = R.getAcceptableDecl(*I))
R.addDecl(ND, AS);
}
R.resolveKind();
@ -2534,7 +2535,7 @@ void Sema::DiagnoseAmbiguousLookup(LookupResult &Result) {
<< Name << SubobjectType << getAmbiguousPathsDisplayString(*Paths)
<< LookupRange;
DeclContext::lookup_iterator Found = Paths->front().Decls.begin();
DeclContext::lookup_iterator Found = Paths->front().Decls;
while (isa<CXXMethodDecl>(*Found) &&
cast<CXXMethodDecl>(*Found)->isStatic())
++Found;
@ -2552,7 +2553,7 @@ void Sema::DiagnoseAmbiguousLookup(LookupResult &Result) {
for (CXXBasePaths::paths_iterator Path = Paths->begin(),
PathEnd = Paths->end();
Path != PathEnd; ++Path) {
const NamedDecl *D = Path->Decls.front();
const NamedDecl *D = *Path->Decls;
if (!D->isInIdentifierNamespace(Result.getIdentifierNamespace()))
continue;
if (DeclsPrinted.insert(D).second) {

View File

@ -112,12 +112,10 @@ CheckPropertyAgainstProtocol(Sema &S, ObjCPropertyDecl *Prop,
return;
// Look for a property with the same name.
DeclContext::lookup_result R = Proto->lookup(Prop->getDeclName());
for (unsigned I = 0, N = R.size(); I != N; ++I) {
if (ObjCPropertyDecl *ProtoProp = dyn_cast<ObjCPropertyDecl>(R[I])) {
S.DiagnosePropertyMismatch(Prop, ProtoProp, Proto->getIdentifier(), true);
return;
}
if (ObjCPropertyDecl *ProtoProp =
Proto->lookup(Prop->getDeclName()).find_first<ObjCPropertyDecl>()) {
S.DiagnosePropertyMismatch(Prop, ProtoProp, Proto->getIdentifier(), true);
return;
}
// Check this property against any protocols we inherit.
@ -233,18 +231,13 @@ Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc,
bool FoundInSuper = false;
ObjCInterfaceDecl *CurrentInterfaceDecl = IFace;
while (ObjCInterfaceDecl *Super = CurrentInterfaceDecl->getSuperClass()) {
DeclContext::lookup_result R = Super->lookup(Res->getDeclName());
for (unsigned I = 0, N = R.size(); I != N; ++I) {
if (ObjCPropertyDecl *SuperProp = dyn_cast<ObjCPropertyDecl>(R[I])) {
DiagnosePropertyMismatch(Res, SuperProp, Super->getIdentifier(), false);
FoundInSuper = true;
break;
}
}
if (FoundInSuper)
if (ObjCPropertyDecl *SuperProp =
Super->lookup(Res->getDeclName()).find_first<ObjCPropertyDecl>()) {
DiagnosePropertyMismatch(Res, SuperProp, Super->getIdentifier(), false);
FoundInSuper = true;
break;
else
CurrentInterfaceDecl = Super;
}
CurrentInterfaceDecl = Super;
}
if (FoundInSuper) {
@ -1149,14 +1142,13 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S,
// redeclared 'readwrite', then no warning is to be issued.
for (auto *Ext : IDecl->known_extensions()) {
DeclContext::lookup_result R = Ext->lookup(property->getDeclName());
if (!R.empty())
if (ObjCPropertyDecl *ExtProp = dyn_cast<ObjCPropertyDecl>(R[0])) {
PIkind = ExtProp->getPropertyAttributesAsWritten();
if (PIkind & ObjCPropertyAttribute::kind_readwrite) {
ReadWriteProperty = true;
break;
}
if (auto *ExtProp = R.find_first<ObjCPropertyDecl>()) {
PIkind = ExtProp->getPropertyAttributesAsWritten();
if (PIkind & ObjCPropertyAttribute::kind_readwrite) {
ReadWriteProperty = true;
break;
}
}
}
if (!ReadWriteProperty) {

View File

@ -3420,7 +3420,8 @@ Sema::InstantiateClassMembers(SourceLocation PointOfInstantiation,
Instantiation->getTemplateInstantiationPattern();
DeclContext::lookup_result Lookup =
ClassPattern->lookup(Field->getDeclName());
FieldDecl *Pattern = cast<FieldDecl>(Lookup.front());
FieldDecl *Pattern = Lookup.find_first<FieldDecl>();
assert(Pattern);
InstantiateInClassInitializer(PointOfInstantiation, Field, Pattern,
TemplateArgs);
}

View File

@ -7649,9 +7649,10 @@ ASTReader::FindExternalVisibleDeclsByName(const DeclContext *DC,
// Load the list of declarations.
SmallVector<NamedDecl *, 64> Decls;
llvm::SmallPtrSet<NamedDecl *, 8> Found;
for (DeclID ID : It->second.Table.find(Name)) {
NamedDecl *ND = cast<NamedDecl>(GetDecl(ID));
if (ND->getDeclName() == Name)
if (ND->getDeclName() == Name && Found.insert(ND).second)
Decls.push_back(ND);
}

View File

@ -13,7 +13,6 @@
#include "ASTCommon.h"
#include "clang/AST/Attr.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclContextInternals.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/DeclVisitor.h"
#include "clang/AST/Expr.h"

View File

@ -842,7 +842,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
llvm::Optional<QualType> operator()(StringRef Name) {
IdentifierInfo &II = ACtx.Idents.get(Name);
auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II);
if (LookupRes.size() == 0)
if (LookupRes.empty())
return None;
// Prioritze typedef declarations.
@ -994,7 +994,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
return false;
IdentifierInfo &II = ACtx.Idents.get(Name);
auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II);
if (LookupRes.size() == 0)
if (LookupRes.empty())
return false;
for (Decl *D : LookupRes) {
if (auto *FD = dyn_cast<FunctionDecl>(D)) {

View File

@ -1,8 +1,10 @@
// RUN: %clang_cc1 -std=c++2a -include %s %s -ast-print -verify | FileCheck %s
//
// RUN: %clang_cc1 -std=c++2a -emit-pch %s -o %t-cxx2a
// RUN: %clang_cc1 -std=c++2a -DUSE_PCH -include-pch %t-cxx2a %s -ast-print -verify | FileCheck %s
// RUN: %clang_cc1 -std=c++2a -include-pch %t-cxx2a %s -ast-print -verify | FileCheck %s
// RUN: %clang_cc1 -std=c++2a -emit-pch -fpch-instantiate-templates %s -o %t-cxx2a
// RUN: %clang_cc1 -std=c++2a -DUSE_PCH -include-pch %t-cxx2a %s -ast-print -verify | FileCheck %s
// RUN: %clang_cc1 -std=c++2a -include-pch %t-cxx2a %s -ast-print -verify | FileCheck %s
#ifndef USE_PCH
namespace inheriting_constructor {
@ -125,3 +127,5 @@ A a1 = { 0 };
#endif
}
#define USE_PCH

View File

@ -1030,7 +1030,7 @@ long long clang_Type_getOffsetOf(CXType PT, const char *S) {
// and we would return InvalidFieldName instead of Incomplete.
// But this erroneous results does protects again a hidden assertion failure
// in the RecordLayoutBuilder
if (Res.size() != 1)
if (!Res.isSingleResult())
return CXTypeLayoutError_InvalidFieldName;
if (const FieldDecl *FD = dyn_cast<FieldDecl>(Res.front()))
return Ctx.getFieldOffset(FD);

View File

@ -2561,9 +2561,9 @@ TEST_P(ImportFriendFunctions, Lookup) {
auto FromName = FromD->getDeclName();
auto *Class = FirstDeclMatcher<CXXRecordDecl>().match(FromTU, ClassPattern);
auto LookupRes = Class->noload_lookup(FromName);
ASSERT_EQ(LookupRes.size(), 0u);
ASSERT_TRUE(LookupRes.empty());
LookupRes = FromTU->noload_lookup(FromName);
ASSERT_EQ(LookupRes.size(), 1u);
ASSERT_TRUE(LookupRes.isSingleResult());
}
auto *ToD = cast<FunctionDecl>(Import(FromD, Lang_CXX03));
@ -2572,9 +2572,9 @@ TEST_P(ImportFriendFunctions, Lookup) {
TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
auto *Class = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, ClassPattern);
auto LookupRes = Class->noload_lookup(ToName);
EXPECT_EQ(LookupRes.size(), 0u);
EXPECT_TRUE(LookupRes.empty());
LookupRes = ToTU->noload_lookup(ToName);
EXPECT_EQ(LookupRes.size(), 1u);
EXPECT_TRUE(LookupRes.isSingleResult());
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, FunctionPattern), 1u);
auto *To0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, FunctionPattern);
@ -2608,9 +2608,9 @@ TEST_P(ImportFriendFunctions, LookupWithProtoAfter) {
auto *FromClass =
FirstDeclMatcher<CXXRecordDecl>().match(FromTU, ClassPattern);
auto LookupRes = FromClass->noload_lookup(FromName);
ASSERT_EQ(LookupRes.size(), 0u);
ASSERT_TRUE(LookupRes.empty());
LookupRes = FromTU->noload_lookup(FromName);
ASSERT_EQ(LookupRes.size(), 1u);
ASSERT_TRUE(LookupRes.isSingleResult());
auto *ToFriend = cast<FunctionDecl>(Import(FromFriend, Lang_CXX03));
auto ToName = ToFriend->getDeclName();
@ -2618,10 +2618,10 @@ TEST_P(ImportFriendFunctions, LookupWithProtoAfter) {
TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
auto *ToClass = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, ClassPattern);
LookupRes = ToClass->noload_lookup(ToName);
EXPECT_EQ(LookupRes.size(), 0u);
EXPECT_TRUE(LookupRes.empty());
LookupRes = ToTU->noload_lookup(ToName);
// Test is disabled because this result is 2.
EXPECT_EQ(LookupRes.size(), 1u);
EXPECT_TRUE(LookupRes.isSingleResult());
ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, FunctionPattern), 2u);
ToFriend = FirstDeclMatcher<FunctionDecl>().match(ToTU, FunctionPattern);
@ -2652,9 +2652,9 @@ TEST_P(ImportFriendFunctions, LookupWithProtoBefore) {
auto *FromClass =
FirstDeclMatcher<CXXRecordDecl>().match(FromTU, ClassPattern);
auto LookupRes = FromClass->noload_lookup(FromName);
ASSERT_EQ(LookupRes.size(), 0u);
ASSERT_TRUE(LookupRes.empty());
LookupRes = FromTU->noload_lookup(FromName);
ASSERT_EQ(LookupRes.size(), 1u);
ASSERT_TRUE(LookupRes.isSingleResult());
auto *ToNormal = cast<FunctionDecl>(Import(FromNormal, Lang_CXX03));
auto ToName = ToNormal->getDeclName();
@ -2662,9 +2662,9 @@ TEST_P(ImportFriendFunctions, LookupWithProtoBefore) {
auto *ToClass = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, ClassPattern);
LookupRes = ToClass->noload_lookup(ToName);
EXPECT_EQ(LookupRes.size(), 0u);
EXPECT_TRUE(LookupRes.empty());
LookupRes = ToTU->noload_lookup(ToName);
EXPECT_EQ(LookupRes.size(), 1u);
EXPECT_TRUE(LookupRes.isSingleResult());
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, FunctionPattern), 2u);
ToNormal = FirstDeclMatcher<FunctionDecl>().match(ToTU, FunctionPattern);
@ -2694,9 +2694,9 @@ TEST_P(ImportFriendFunctions, ImportFriendChangesLookup) {
ASSERT_FALSE(FromFriendF->isInIdentifierNamespace(Decl::IDNS_Ordinary));
ASSERT_TRUE(FromFriendF->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
auto LookupRes = FromNormalTU->noload_lookup(FromNormalName);
ASSERT_EQ(LookupRes.size(), 1u);
ASSERT_TRUE(LookupRes.isSingleResult());
LookupRes = FromFriendTU->noload_lookup(FromFriendName);
ASSERT_EQ(LookupRes.size(), 1u);
ASSERT_TRUE(LookupRes.isSingleResult());
auto *ToNormalF = cast<FunctionDecl>(Import(FromNormalF, Lang_CXX03));
TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
@ -2704,12 +2704,12 @@ TEST_P(ImportFriendFunctions, ImportFriendChangesLookup) {
EXPECT_TRUE(ToNormalF->isInIdentifierNamespace(Decl::IDNS_Ordinary));
EXPECT_FALSE(ToNormalF->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
LookupRes = ToTU->noload_lookup(ToName);
EXPECT_EQ(LookupRes.size(), 1u);
EXPECT_TRUE(LookupRes.isSingleResult());
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 1u);
auto *ToFriendF = cast<FunctionDecl>(Import(FromFriendF, Lang_CXX03));
LookupRes = ToTU->noload_lookup(ToName);
EXPECT_EQ(LookupRes.size(), 1u);
EXPECT_TRUE(LookupRes.isSingleResult());
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
EXPECT_TRUE(ToNormalF->isInIdentifierNamespace(Decl::IDNS_Ordinary));
@ -4031,11 +4031,11 @@ TEST_P(DeclContextTest,
ASSERT_TRUE(L.getAsDecl());
// Simulate the private function DeclContext::reconcileExternalVisibleStorage.
// The point here is to have a Vec with only one element, which is not the
// one we are going to delete from the DC later.
// We do not have a list with one element.
L.setHasExternalDecls();
ASSERT_TRUE(L.getAsVector());
ASSERT_EQ(1u, L.getAsVector()->size());
ASSERT_FALSE(L.getAsList());
auto Results = L.getLookupResult();
ASSERT_EQ(1u, std::distance(Results.begin(), Results.end()));
// This asserts in the old implementation.
DC->removeDecl(A0);

View File

@ -59,7 +59,7 @@ public:
clang::DeclContext::lookup_result result =
non_const_interface_decl->lookup(name);
return (result.size() != 0);
return (!result.empty());
} while (false);
SetNoExternalVisibleDeclsForName(decl_ctx, name);
@ -555,7 +555,7 @@ uint32_t AppleObjCDeclVendor::FindDecls(ConstString name, bool append,
if (!lookup_result.empty()) {
if (clang::ObjCInterfaceDecl *result_iface_decl =
llvm::dyn_cast<clang::ObjCInterfaceDecl>(lookup_result[0])) {
llvm::dyn_cast<clang::ObjCInterfaceDecl>(*lookup_result.begin())) {
if (log) {
clang::QualType result_iface_type =
ast_ctx.getObjCInterfaceType(result_iface_decl);

View File

@ -326,7 +326,7 @@ GetDeclFromContextByName(const clang::ASTContext &ast,
if (result.empty())
return nullptr;
return result[0];
return *result.begin();
}
static bool IsAnonymousNamespaceName(llvm::StringRef name) {

View File

@ -163,7 +163,6 @@ void addOverridesForMethod(clang::CXXMethodDecl *decl) {
if (name.getNameKind() == clang::DeclarationName::CXXDestructorName)
if (auto *baseDtorDecl = base_record->getDestructor()) {
if (baseDtorDecl->isVirtual()) {
path.Decls = baseDtorDecl;
decls.push_back(baseDtorDecl);
return true;
} else
@ -171,12 +170,11 @@ void addOverridesForMethod(clang::CXXMethodDecl *decl) {
}
// Otherwise, search for name in the base class.
for (path.Decls = base_record->lookup(name); !path.Decls.empty();
path.Decls = path.Decls.slice(1)) {
for (path.Decls = base_record->lookup(name).begin();
path.Decls != path.Decls.end(); ++path.Decls) {
if (auto *method_decl =
llvm::dyn_cast<clang::CXXMethodDecl>(path.Decls.front()))
llvm::dyn_cast<clang::CXXMethodDecl>(*path.Decls))
if (method_decl->isVirtual() && !isOverload(decl, method_decl)) {
path.Decls = method_decl;
decls.push_back(method_decl);
return true;
}
@ -6605,10 +6603,11 @@ size_t TypeSystemClang::GetIndexOfChildMemberWithName(
if (cxx_record_decl->lookupInBases(
[decl_name](const clang::CXXBaseSpecifier *specifier,
clang::CXXBasePath &path) {
path.Decls =
specifier->getType()->getAsCXXRecordDecl()->lookup(
decl_name);
return !path.Decls.empty();
CXXRecordDecl *record =
specifier->getType()->getAsCXXRecordDecl();
auto r = record->lookup(decl_name);
path.Decls = r.begin();
return !r.empty();
},
paths)) {
clang::CXXBasePaths::const_paths_iterator path,
@ -6631,9 +6630,10 @@ size_t TypeSystemClang::GetIndexOfChildMemberWithName(
->getDecl());
}
}
for (clang::NamedDecl *path_decl : path->Decls) {
for (clang::DeclContext::lookup_iterator I = path->Decls, E;
I != E; ++I) {
child_idx = GetIndexForRecordChild(
parent_record_decl, path_decl, omit_empty_base_classes);
parent_record_decl, *I, omit_empty_base_classes);
if (child_idx == UINT32_MAX) {
child_indexes.clear();
return 0;

View File

@ -265,7 +265,7 @@ public:
clang::DeclContext::lookup_result result = decl_context->lookup(myName);
if (!result.empty()) {
clang::NamedDecl *named_decl = result[0];
clang::NamedDecl *named_decl = *result.begin();
if (const RecordDeclType *record_decl =
llvm::dyn_cast<RecordDeclType>(named_decl))
compiler_type.SetCompilerType(