aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/clang/AST/ASTContext.h4
-rw-r--r--include/clang/AST/DeclBase.h21
-rw-r--r--include/clang/AST/DeclContextInternals.h25
-rw-r--r--include/clang/AST/DependentDiagnostic.h180
-rw-r--r--lib/AST/ASTContext.cpp3
-rw-r--r--lib/AST/DeclBase.cpp101
-rw-r--r--lib/Sema/Sema.h6
-rw-r--r--lib/Sema/SemaAccess.cpp348
-rw-r--r--lib/Sema/SemaTemplateInstantiateDecl.cpp17
-rw-r--r--test/CXX/temp/temp.decls/temp.friend/p1.cpp32
10 files changed, 648 insertions, 89 deletions
diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h
index 4c163611ac..cbe98aa5bb 100644
--- a/include/clang/AST/ASTContext.h
+++ b/include/clang/AST/ASTContext.h
@@ -57,6 +57,7 @@ namespace clang {
class ObjCIvarRefExpr;
class ObjCPropertyDecl;
class RecordDecl;
+ class StoredDeclsMap;
class TagDecl;
class TemplateTypeParmDecl;
class TranslationUnitDecl;
@@ -1272,9 +1273,8 @@ private:
// FIXME: This currently contains the set of StoredDeclMaps used
// by DeclContext objects. This probably should not be in ASTContext,
// but we include it here so that ASTContext can quickly deallocate them.
- std::vector<void*> SDMs;
+ llvm::PointerIntPair<StoredDeclsMap*,1> LastSDM;
friend class DeclContext;
- void *CreateStoredDeclsMap();
void ReleaseDeclContextMaps();
};
diff --git a/include/clang/AST/DeclBase.h b/include/clang/AST/DeclBase.h
index 6f8284458f..e9f004de7b 100644
--- a/include/clang/AST/DeclBase.h
+++ b/include/clang/AST/DeclBase.h
@@ -41,6 +41,8 @@ class LinkageSpecDecl;
class BlockDecl;
class DeclarationName;
class CompoundStmt;
+class StoredDeclsMap;
+class DependentDiagnostic;
}
namespace llvm {
@@ -545,9 +547,9 @@ class DeclContext {
mutable bool ExternalVisibleStorage : 1;
/// \brief Pointer to the data structure used to lookup declarations
- /// within this context, which is a DenseMap<DeclarationName,
- /// StoredDeclsList>.
- mutable void* LookupPtr;
+ /// within this context (or a DependentStoredDeclsMap if this is a
+ /// dependent context).
+ mutable StoredDeclsMap *LookupPtr;
/// FirstDecl - The first declaration stored within this declaration
/// context.
@@ -674,6 +676,9 @@ public:
/// "primary" DeclContext structure, which will contain the
/// information needed to perform name lookup into this context.
DeclContext *getPrimaryContext();
+ const DeclContext *getPrimaryContext() const {
+ return const_cast<DeclContext*>(this)->getPrimaryContext();
+ }
/// getLookupContext - Retrieve the innermost non-transparent
/// context of this context, which corresponds to the innermost
@@ -976,10 +981,15 @@ public:
return getUsingDirectives().second;
}
+ // These are all defined in DependentDiagnostic.h.
+ class ddiag_iterator;
+ inline ddiag_iterator ddiag_begin() const;
+ inline ddiag_iterator ddiag_end() const;
+
// Low-level accessors
/// \brief Retrieve the internal representation of the lookup structure.
- void* getLookupPtr() const { return LookupPtr; }
+ StoredDeclsMap* getLookupPtr() const { return LookupPtr; }
/// \brief Whether this DeclContext has external storage containing
/// additional declarations that are lexically in this context.
@@ -1013,6 +1023,9 @@ private:
void LoadLexicalDeclsFromExternalStorage() const;
void LoadVisibleDeclsFromExternalStorage() const;
+ friend class DependentDiagnostic;
+ StoredDeclsMap *CreateStoredDeclsMap(ASTContext &C) const;
+
void buildLookup(DeclContext *DCtx);
void makeDeclVisibleInContextImpl(NamedDecl *D);
};
diff --git a/include/clang/AST/DeclContextInternals.h b/include/clang/AST/DeclContextInternals.h
index 32405ee812..132ca87a5f 100644
--- a/include/clang/AST/DeclContextInternals.h
+++ b/include/clang/AST/DeclContextInternals.h
@@ -24,6 +24,8 @@
namespace clang {
+class DependentDiagnostic;
+
/// StoredDeclsList - This is an array of decls optimized a common case of only
/// containing one entry.
struct StoredDeclsList {
@@ -258,8 +260,29 @@ public:
}
};
-typedef llvm::DenseMap<DeclarationName, StoredDeclsList> StoredDeclsMap;
+class StoredDeclsMap
+ : public llvm::DenseMap<DeclarationName, StoredDeclsList> {
+
+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;
+};
+class DependentStoredDeclsMap : public StoredDeclsMap {
+public:
+ DependentStoredDeclsMap() : FirstDiagnostic(0) {}
+ ~DependentStoredDeclsMap();
+
+private:
+ friend class DependentDiagnostic;
+ friend class DeclContext; // iterates over diagnostics
+
+ DependentDiagnostic *FirstDiagnostic;
+};
} // end namespace clang
diff --git a/include/clang/AST/DependentDiagnostic.h b/include/clang/AST/DependentDiagnostic.h
new file mode 100644
index 0000000000..fb119a9f45
--- /dev/null
+++ b/include/clang/AST/DependentDiagnostic.h
@@ -0,0 +1,180 @@
+//===-- DependentDiagnostic.h - Dependently-generated diagnostics -*- C++ -*-=//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines interfaces for diagnostics which may or may
+// fire based on how a template is instantiated.
+//
+// At the moment, the only consumer of this interface is access
+// control.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_DEPENDENT_DIAGNOSTIC_H
+#define LLVM_CLANG_AST_DEPENDENT_DIAGNOSTIC_H
+
+#include "clang/Basic/PartialDiagnostic.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/AST/DeclBase.h"
+#include "clang/AST/DeclContextInternals.h"
+
+namespace clang {
+
+class ASTContext;
+class CXXRecordDecl;
+class NamedDecl;
+
+/// A dependently-generated diagnostic.
+class DependentDiagnostic {
+public:
+ enum AccessNonce { Access = 0 };
+
+ static DependentDiagnostic *Create(ASTContext &Context,
+ DeclContext *Parent,
+ AccessNonce _,
+ SourceLocation Loc,
+ bool IsMemberAccess,
+ AccessSpecifier AS,
+ NamedDecl *TargetDecl,
+ CXXRecordDecl *NamingClass,
+ const PartialDiagnostic &PDiag) {
+ DependentDiagnostic *DD = Create(Context, Parent, PDiag);
+ DD->AccessData.Loc = Loc.getRawEncoding();
+ DD->AccessData.IsMember = IsMemberAccess;
+ DD->AccessData.Access = AS;
+ DD->AccessData.TargetDecl = TargetDecl;
+ DD->AccessData.NamingClass = NamingClass;
+ return DD;
+ }
+
+ unsigned getKind() const {
+ return Access;
+ }
+
+ bool isAccessToMember() const {
+ assert(getKind() == Access);
+ return AccessData.IsMember;
+ }
+
+ AccessSpecifier getAccess() const {
+ assert(getKind() == Access);
+ return AccessSpecifier(AccessData.Access);
+ }
+
+ SourceLocation getAccessLoc() const {
+ assert(getKind() == Access);
+ return SourceLocation::getFromRawEncoding(AccessData.Loc);
+ }
+
+ NamedDecl *getAccessTarget() const {
+ assert(getKind() == Access);
+ return AccessData.TargetDecl;
+ }
+
+ NamedDecl *getAccessNamingClass() const {
+ assert(getKind() == Access);
+ return AccessData.NamingClass;
+ }
+
+ const PartialDiagnostic &getDiagnostic() const {
+ return Diag;
+ }
+
+private:
+ DependentDiagnostic(const PartialDiagnostic &PDiag) : Diag(PDiag) {}
+ static DependentDiagnostic *Create(ASTContext &Context,
+ DeclContext *Parent,
+ const PartialDiagnostic &PDiag);
+
+ friend class DependentStoredDeclsMap;
+ friend class DeclContext::ddiag_iterator;
+ DependentDiagnostic *NextDiagnostic;
+
+ PartialDiagnostic Diag;
+
+ union {
+ struct {
+ unsigned Loc;
+ unsigned Access : 2;
+ unsigned IsMember : 1;
+ NamedDecl *TargetDecl;
+ CXXRecordDecl *NamingClass;
+ } AccessData;
+ };
+};
+
+///
+
+/// An iterator over the dependent diagnostics in a dependent context.
+class DeclContext::ddiag_iterator {
+public:
+ ddiag_iterator() : Ptr(0) {}
+ explicit ddiag_iterator(DependentDiagnostic *Ptr) : Ptr(Ptr) {}
+
+ typedef DependentDiagnostic *value_type;
+ typedef DependentDiagnostic *reference;
+ typedef DependentDiagnostic *pointer;
+ typedef int difference_type;
+ typedef std::forward_iterator_tag iterator_category;
+
+ reference operator*() const { return Ptr; }
+
+ ddiag_iterator &operator++() {
+ assert(Ptr && "attempt to increment past end of diag list");
+ Ptr = Ptr->NextDiagnostic;
+ return *this;
+ }
+
+ ddiag_iterator operator++(int) {
+ ddiag_iterator tmp = *this;
+ ++*this;
+ return tmp;
+ }
+
+ bool operator==(ddiag_iterator Other) const {
+ return Ptr == Other.Ptr;
+ }
+
+ bool operator!=(ddiag_iterator Other) const {
+ return Ptr != Other.Ptr;
+ }
+
+ ddiag_iterator &operator+=(difference_type N) {
+ assert(N >= 0 && "cannot rewind a DeclContext::ddiag_iterator");
+ while (N--)
+ ++*this;
+ return *this;
+ }
+
+ ddiag_iterator operator+(difference_type N) const {
+ ddiag_iterator tmp = *this;
+ tmp += N;
+ return tmp;
+ }
+
+private:
+ DependentDiagnostic *Ptr;
+};
+
+inline DeclContext::ddiag_iterator DeclContext::ddiag_begin() const {
+ assert(isDependentContext()
+ && "cannot iterate dependent diagnostics of non-dependent context");
+ const DependentStoredDeclsMap *Map
+ = static_cast<DependentStoredDeclsMap*>(getPrimaryContext()->LookupPtr);
+
+ if (!Map) return ddiag_iterator();
+ return ddiag_iterator(Map->FirstDiagnostic);
+}
+
+inline DeclContext::ddiag_iterator DeclContext::ddiag_end() const {
+ return ddiag_iterator();
+}
+
+}
+
+#endif
diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp
index fcc3e3d0c2..7f2e35b263 100644
--- a/lib/AST/ASTContext.cpp
+++ b/lib/AST/ASTContext.cpp
@@ -45,7 +45,8 @@ ASTContext::ASTContext(const LangOptions& LOpts, SourceManager &SM,
sigjmp_bufDecl(0), BlockDescriptorType(0), BlockDescriptorExtendedType(0),
SourceMgr(SM), LangOpts(LOpts), FreeMemory(FreeMem), Target(t),
Idents(idents), Selectors(sels),
- BuiltinInfo(builtins), ExternalSource(0), PrintingPolicy(LOpts) {
+ BuiltinInfo(builtins), ExternalSource(0), PrintingPolicy(LOpts),
+ LastSDM(0, 0) {
ObjCIdRedefinitionType = QualType();
ObjCClassRedefinitionType = QualType();
ObjCSelRedefinitionType = QualType();
diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp
index 1aac7cfd59..9e92855681 100644
--- a/lib/AST/DeclBase.cpp
+++ b/lib/AST/DeclBase.cpp
@@ -18,6 +18,7 @@
#include "clang/AST/DeclFriend.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/DependentDiagnostic.h"
#include "clang/AST/ExternalASTSource.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Type.h"
@@ -481,7 +482,7 @@ DeclContext::~DeclContext() {
// FIXME: Currently ~ASTContext will delete the StoredDeclsMaps because
// ~DeclContext() is not guaranteed to be called when ASTContext uses
// a BumpPtrAllocator.
- // delete static_cast<StoredDeclsMap*>(LookupPtr);
+ // delete LookupPtr;
}
void DeclContext::DestroyDecls(ASTContext &C) {
@@ -516,10 +517,16 @@ bool DeclContext::isDependentContext() const {
if (Record->getDescribedClassTemplate())
return true;
- if (const FunctionDecl *Function = dyn_cast<FunctionDecl>(this))
+ if (const FunctionDecl *Function = dyn_cast<FunctionDecl>(this)) {
if (Function->getDescribedFunctionTemplate())
return true;
+ // Friend function declarations are dependent if their *lexical*
+ // context is dependent.
+ if (cast<Decl>(this)->getFriendObjectKind())
+ return getLexicalParent()->isDependentContext();
+ }
+
return getParent() && getParent()->isDependentContext();
}
@@ -666,9 +673,7 @@ DeclContext::LoadVisibleDeclsFromExternalStorage() const {
// Load the declaration IDs for all of the names visible in this
// context.
assert(!LookupPtr && "Have a lookup map before de-serialization?");
- StoredDeclsMap *Map =
- (StoredDeclsMap*) getParentASTContext().CreateStoredDeclsMap();
- LookupPtr = Map;
+ StoredDeclsMap *Map = CreateStoredDeclsMap(getParentASTContext());
for (unsigned I = 0, N = Decls.size(); I != N; ++I) {
(*Map)[Decls[I].Name].setFromDeclIDs(Decls[I].Declarations);
}
@@ -727,10 +732,9 @@ void DeclContext::removeDecl(Decl *D) {
if (isa<NamedDecl>(D)) {
NamedDecl *ND = cast<NamedDecl>(D);
- void *OpaqueMap = getPrimaryContext()->LookupPtr;
- if (!OpaqueMap) return;
+ StoredDeclsMap *Map = getPrimaryContext()->LookupPtr;
+ if (!Map) return;
- StoredDeclsMap *Map = static_cast<StoredDeclsMap*>(OpaqueMap);
StoredDeclsMap::iterator Pos = Map->find(ND->getDeclName());
assert(Pos != Map->end() && "no lookup entry for decl");
Pos->second.remove(ND);
@@ -808,9 +812,8 @@ DeclContext::lookup(DeclarationName Name) {
return lookup_result(0, 0);
}
- StoredDeclsMap *Map = static_cast<StoredDeclsMap*>(LookupPtr);
- StoredDeclsMap::iterator Pos = Map->find(Name);
- if (Pos == Map->end())
+ StoredDeclsMap::iterator Pos = LookupPtr->find(Name);
+ if (Pos == LookupPtr->end())
return lookup_result(0, 0);
return Pos->second.getLookupResult(getParentASTContext());
}
@@ -878,12 +881,11 @@ void DeclContext::makeDeclVisibleInContextImpl(NamedDecl *D) {
ASTContext *C = 0;
if (!LookupPtr) {
C = &getParentASTContext();
- LookupPtr = (StoredDeclsMap*) C->CreateStoredDeclsMap();
+ CreateStoredDeclsMap(*C);
}
// Insert this declaration into the map.
- StoredDeclsMap &Map = *static_cast<StoredDeclsMap*>(LookupPtr);
- StoredDeclsList &DeclNameEntries = Map[D->getDeclName()];
+ StoredDeclsList &DeclNameEntries = (*LookupPtr)[D->getDeclName()];
if (DeclNameEntries.isNull()) {
DeclNameEntries.setOnlyValue(D);
return;
@@ -952,13 +954,74 @@ void StoredDeclsList::materializeDecls(ASTContext &Context) {
// Creation and Destruction of StoredDeclsMaps. //
//===----------------------------------------------------------------------===//
-void *ASTContext::CreateStoredDeclsMap() {
- StoredDeclsMap *M = new StoredDeclsMap();
- SDMs.push_back(M);
+StoredDeclsMap *DeclContext::CreateStoredDeclsMap(ASTContext &C) const {
+ assert(!LookupPtr && "context already has a decls map");
+ assert(getPrimaryContext() == this &&
+ "creating decls map on non-primary context");
+
+ StoredDeclsMap *M;
+ bool Dependent = isDependentContext();
+ if (Dependent)
+ M = new DependentStoredDeclsMap();
+ else
+ M = new StoredDeclsMap();
+ M->Previous = C.LastSDM;
+ C.LastSDM = llvm::PointerIntPair<StoredDeclsMap*,1>(M, Dependent);
+ LookupPtr = M;
return M;
}
void ASTContext::ReleaseDeclContextMaps() {
- for (std::vector<void*>::iterator I = SDMs.begin(), E = SDMs.end(); I!=E; ++I)
- delete (StoredDeclsMap*) *I;
+ // It's okay to delete DependentStoredDeclsMaps via a StoredDeclsMap
+ // pointer because the subclass doesn't add anything that needs to
+ // be deleted.
+
+ StoredDeclsMap::DestroyAll(LastSDM.getPointer(), LastSDM.getInt());
+}
+
+void StoredDeclsMap::DestroyAll(StoredDeclsMap *Map, bool Dependent) {
+ while (Map) {
+ // Advance the iteration before we invalidate memory.
+ llvm::PointerIntPair<StoredDeclsMap*,1> Next = Map->Previous;
+
+ if (Dependent)
+ delete static_cast<DependentStoredDeclsMap*>(Map);
+ else
+ delete Map;
+
+ Map = Next.getPointer();
+ Dependent = Next.getInt();
+ }
+}
+
+DependentStoredDeclsMap::~DependentStoredDeclsMap() {
+ // Kill off the dependent diagnostics. They don't need to be
+ // deleted, but they do need to be destructed.
+ DependentDiagnostic *CurD = FirstDiagnostic;
+ while (CurD) {
+ DependentDiagnostic *NextD = CurD->NextDiagnostic;
+ CurD->~DependentDiagnostic();
+ CurD = NextD;
+ }
+}
+
+DependentDiagnostic *DependentDiagnostic::Create(ASTContext &C,
+ DeclContext *Parent,
+ const PartialDiagnostic &PDiag) {
+ assert(Parent->isDependentContext()
+ && "cannot iterate dependent diagnostics of non-dependent context");
+ Parent = Parent->getPrimaryContext();
+ if (!Parent->LookupPtr)
+ Parent->CreateStoredDeclsMap(C);
+
+ DependentStoredDeclsMap *Map
+ = static_cast<DependentStoredDeclsMap*>(Parent->LookupPtr);
+
+ DependentDiagnostic *DD = new (C) DependentDiagnostic(PDiag);
+
+ // TODO: Maybe we shouldn't reverse the order during insertion.
+ DD->NextDiagnostic = Map->FirstDiagnostic;
+ Map->FirstDiagnostic = DD;
+
+ return DD;
}
diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h
index 23c74a854f..85fa4f1742 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -2648,9 +2648,13 @@ public:
unsigned DiagID,
bool ForceCheck = false,
bool ForceUnprivileged = false);
-
void CheckLookupAccess(const LookupResult &R);
+ void HandleDependentAccessCheck(const DependentDiagnostic &DD,
+ const MultiLevelTemplateArgumentList &TemplateArgs);
+ void PerformDependentDiagnostics(const DeclContext *Pattern,
+ const MultiLevelTemplateArgumentList &TemplateArgs);
+
void HandleDelayedAccessCheck(DelayedDiagnostic &DD, Decl *Ctx);
enum AbstractDiagSelID {
diff --git a/lib/Sema/SemaAccess.cpp b/lib/Sema/SemaAccess.cpp
index 40b320c1be..e74c8f60c3 100644
--- a/lib/Sema/SemaAccess.cpp
+++ b/lib/Sema/SemaAccess.cpp
@@ -17,6 +17,7 @@
#include "clang/AST/CXXInheritance.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclFriend.h"
+#include "clang/AST/DependentDiagnostic.h"
#include "clang/AST/ExprCXX.h"
using namespace clang;
@@ -52,9 +53,11 @@ bool Sema::SetMemberAccessSpecifier(NamedDecl *MemberDecl,
namespace {
struct EffectiveContext {
- EffectiveContext() : Function(0) {}
+ EffectiveContext() : Function(0), Dependent(false) {}
explicit EffectiveContext(DeclContext *DC) {
+ Dependent = DC->isDependentContext();
+
if (isa<FunctionDecl>(DC)) {
Function = cast<FunctionDecl>(DC)->getCanonicalDecl();
DC = Function->getDeclContext();
@@ -75,14 +78,25 @@ struct EffectiveContext {
}
}
+ bool isDependent() const { return Dependent; }
+
bool includesClass(const CXXRecordDecl *R) const {
R = R->getCanonicalDecl();
return std::find(Records.begin(), Records.end(), R)
!= Records.end();
}
+ DeclContext *getPrimaryContext() const {
+ assert((Function || !Records.empty()) && "context has no primary context");
+ if (Function) return Function;
+ return Records[0];
+ }
+
+ typedef llvm::SmallVectorImpl<CXXRecordDecl*>::const_iterator record_iterator;
+
llvm::SmallVector<CXXRecordDecl*, 4> Records;
FunctionDecl *Function;
+ bool Dependent;
};
}
@@ -93,88 +107,234 @@ static CXXRecordDecl *FindDeclaringClass(NamedDecl *D) {
return DeclaringClass;
}
+static bool MightInstantiateTo(Sema &S, DeclContext *Context,
+ DeclContext *Friend) {
+ if (Friend == Context)
+ return true;
+
+ assert(!Friend->isDependentContext() &&
+ "can't handle friends with dependent contexts here");
+
+ if (!Context->isDependentContext())
+ return false;
+
+ if (Friend->isFileContext())
+ return false;
+
+ // TODO: this is very conservative
+ return true;
+}
+
+// Asks whether the type in 'context' can ever instantiate to the type
+// in 'friend'.
+static bool MightInstantiateTo(Sema &S, CanQualType Context, CanQualType Friend) {
+ if (Friend == Context)
+ return true;
+
+ if (!Friend->isDependentType() && !Context->isDependentType())
+ return false;
+
+ // TODO: this is very conservative.
+ return true;
+}
+
+static bool MightInstantiateTo(Sema &S,
+ FunctionDecl *Context,
+ FunctionDecl *Friend) {
+ if (Context->getDeclName() != Friend->getDeclName())
+ return false;
+
+ if (!MightInstantiateTo(S,
+ Context->getDeclContext(),
+ Friend->getDeclContext()))
+ return false;
+
+ CanQual<FunctionProtoType> FriendTy
+ = S.Context.getCanonicalType(Friend->getType())
+ ->getAs<FunctionProtoType>();
+ CanQual<FunctionProtoType> ContextTy
+ = S.Context.getCanonicalType(Context->getType())
+ ->getAs<FunctionProtoType>();
+
+ // There isn't any way that I know of to add qualifiers
+ // during instantiation.
+ if (FriendTy.getQualifiers() != ContextTy.getQualifiers())
+ return false;
+
+ if (FriendTy->getNumArgs() != ContextTy->getNumArgs())
+ return false;
+
+ if (!MightInstantiateTo(S,
+ ContextTy->getResultType(),
+ FriendTy->getResultType()))
+ return false;
+
+ for (unsigned I = 0, E = FriendTy->getNumArgs(); I != E; ++I)
+ if (!MightInstantiateTo(S,
+ ContextTy->getArgType(I),
+ FriendTy->getArgType(I)))
+ return false;
+
+ return true;
+}
+
+static bool MightInstantiateTo(Sema &S,
+ FunctionTemplateDecl *Context,
+ FunctionTemplateDecl *Friend) {
+ return MightInstantiateTo(S,
+ Context->getTemplatedDecl(),
+ Friend->getTemplatedDecl());
+}
+
static Sema::AccessResult MatchesFriend(Sema &S,
const EffectiveContext &EC,
const CXXRecordDecl *Friend) {
- // FIXME: close matches becuse of dependency
if (EC.includesClass(Friend))
return Sema::AR_accessible;
+ if (EC.isDependent()) {
+ CanQualType FriendTy
+ = S.Context.getCanonicalType(S.Context.getTypeDeclType(Friend));
+
+ for (EffectiveContext::record_iterator
+ I = EC.Records.begin(), E = EC.Records.end(); I != E; ++I) {
+ CanQualType ContextTy
+ = S.Context.getCanonicalType(S.Context.getTypeDeclType(*I));
+ if (MightInstantiateTo(S, ContextTy, FriendTy))
+ return Sema::AR_dependent;
+ }
+ }
+
return Sema::AR_inaccessible;
}
static Sema::AccessResult MatchesFriend(Sema &S,
const EffectiveContext &EC,
- FriendDecl *Friend) {
- if (Type *T = Friend->getFriendType()) {
- CanQualType CT = T->getCanonicalTypeUnqualified();
- if (const RecordType *RT = CT->getAs<RecordType>())
- return MatchesFriend(S, EC, cast<CXXRecordDecl>(RT->getDecl()));
-
- // TODO: we can fail early for a lot of type classes.
- if (T->isDependentType())
- return Sema::AR_dependent;
+ CanQualType Friend) {
+ if (const RecordType *RT = Friend->getAs<RecordType>())
+ return MatchesFriend(S, EC, cast<CXXRecordDecl>(RT->getDecl()));
- return Sema::AR_inaccessible;
- }
+ // TODO: we can do better than this
+ if (Friend->isDependentType())
+ return Sema::AR_dependent;
- NamedDecl *D
- = cast<NamedDecl>(Friend->getFriendDecl()->getCanonicalDecl());
+ return Sema::AR_inaccessible;
+}
- // FIXME: declarations with dependent or templated scope.
+/// Determines whether the given friend class template matches
+/// anything in the effective context.
+static Sema::AccessResult MatchesFriend(Sema &S,
+ const EffectiveContext &EC,
+ ClassTemplateDecl *Friend) {
+ Sema::AccessResult OnFailure = Sema::AR_inaccessible;
- // For class templates, we want to check whether any of the records
- // are possible specializations of the template.
- if (isa<ClassTemplateDecl>(D)) {
- for (llvm::SmallVectorImpl<CXXRecordDecl*>::const_iterator
- I = EC.Records.begin(), E = EC.Records.end(); I != E; ++I) {
- CXXRecordDecl *Record = *I;
- ClassTemplateDecl *CTD;
+ for (llvm::SmallVectorImpl<CXXRecordDecl*>::const_iterator
+ I = EC.Records.begin(), E = EC.Records.end(); I != E; ++I) {
+ CXXRecordDecl *Record = *I;
- // A specialization of the template...
- if (isa<ClassTemplateSpecializationDecl>(Record)) {
- CTD = cast<ClassTemplateSpecializationDecl>(Record)
- ->getSpecializedTemplate();
+ // Check whether the friend is the template of a class in the
+ // context chain. To do that, we need to figure out whether the
+ // current class has a template:
+ ClassTemplateDecl *CTD;
- // ... or the template pattern itself.
- } else {
- CTD = Record->getDescribedClassTemplate();
- }
+ // A specialization of the template...
+ if (isa<ClassTemplateSpecializationDecl>(Record)) {
+ CTD = cast<ClassTemplateSpecializationDecl>(Record)
+ ->getSpecializedTemplate();
- if (CTD && D == CTD->getCanonicalDecl())
- return Sema::AR_accessible;
+ // ... or the template pattern itself.
+ } else {
+ CTD = Record->getDescribedClassTemplate();
+ if (!CTD) continue;
}
- return Sema::AR_inaccessible;
+ // It's a match.
+ if (Friend == CTD->getCanonicalDecl())
+ return Sema::AR_accessible;
+
+ // If the template names don't match, it can't be a dependent
+ // match. This isn't true in C++0x because of template aliases.
+ if (!S.LangOpts.CPlusPlus0x && CTD->getDeclName() != Friend->getDeclName())
+ continue;
+
+ // If the class's context can't instantiate to the friend's
+ // context, it can't be a dependent match.
+ if (!MightInstantiateTo(S, CTD->getDeclContext(),
+ Friend->getDeclContext()))
+ continue;
+
+ // Otherwise, it's a dependent match.
+ OnFailure = Sema::AR_dependent;
}
- // Same thing for function templates.
- if (isa<FunctionTemplateDecl>(D)) {
- if (!EC.Function) return Sema::AR_inaccessible;
+ return OnFailure;
+}
- FunctionTemplateDecl *FTD = EC.Function->getPrimaryTemplate();
- if (!FTD)
- FTD = EC.Function->getDescribedFunctionTemplate();
+/// Determines whether the given friend function matches anything in
+/// the effective context.
+static Sema::AccessResult MatchesFriend(Sema &S,
+ const EffectiveContext &EC,
+ FunctionDecl *Friend) {
+ if (!EC.Function)
+ return Sema::AR_inaccessible;
- if (FTD && D == FTD->getCanonicalDecl())
- return Sema::AR_accessible;
-
+ if (Friend == EC.Function)
+ return Sema::AR_accessible;
+
+ if (EC.isDependent() && MightInstantiateTo(S, EC.Function, Friend))
+ return Sema::AR_dependent;
+
+ return Sema::AR_inaccessible;
+}
+
+/// Determines whether the given friend function template matches
+/// anything in the effective context.
+static Sema::AccessResult MatchesFriend(Sema &S,
+ const EffectiveContext &EC,
+ FunctionTemplateDecl *Friend) {
+ if (!EC.Function) return Sema::AR_inaccessible;
+
+ FunctionTemplateDecl *FTD = EC.Function->getPrimaryTemplate();
+ if (!FTD)
+ FTD = EC.Function->getDescribedFunctionTemplate();
+ if (!FTD)
return Sema::AR_inaccessible;
- }
- // Friend functions. FIXME: close matches due to dependency.
- //
- // The decl pointers in EC have been canonicalized, so pointer
- // equality is sufficient.
- if (D == EC.Function)
+ if (Friend == FTD->getCanonicalDecl())
return Sema::AR_accessible;
- if (isa<CXXRecordDecl>(D))
- return MatchesFriend(S, EC, cast<CXXRecordDecl>(D));
+ if (MightInstantiateTo(S, FTD, Friend))
+ return Sema::AR_dependent;
return Sema::AR_inaccessible;
}
+/// Determines whether the given friend declaration matches anything
+/// in the effective context.
+static Sema::AccessResult MatchesFriend(Sema &S,
+ const EffectiveContext &EC,
+ FriendDecl *FriendD) {
+ if (Type *T = FriendD->getFriendType())
+ return MatchesFriend(S, EC, T->getCanonicalTypeUnqualified());
+
+ NamedDecl *Friend
+ = cast<NamedDecl>(FriendD->getFriendDecl()->getCanonicalDecl());
+
+ // FIXME: declarations with dependent or templated scope.
+
+ if (isa<ClassTemplateDecl>(Friend))
+ return MatchesFriend(S, EC, cast<ClassTemplateDecl>(Friend));
+
+ if (isa<FunctionTemplateDecl>(Friend))
+ return MatchesFriend(S, EC, cast<FunctionTemplateDecl>(Friend));
+
+ if (isa<CXXRecordDecl>(Friend))
+ return MatchesFriend(S, EC, cast<CXXRecordDecl>(Friend));
+
+ assert(isa<FunctionDecl>(Friend) && "unknown friend decl kind");
+ return MatchesFriend(S, EC, cast<FunctionDecl>(Friend));
+}
+
static Sema::AccessResult GetFriendKind(Sema &S,
const EffectiveContext &EC,
const CXXRecordDecl *Class) {
@@ -230,6 +390,8 @@ static CXXBasePath *FindBestPath(Sema &S,
assert(FinalAccess != AS_none && "forbidden access after declaring class");
+ bool AnyDependent = false;
+
// Derive the friend-modified access along each path.
for (CXXBasePaths::paths_iterator PI = Paths.begin(), PE = Paths.end();
PI != PE; ++PI) {
@@ -260,7 +422,8 @@ static CXXBasePath *FindBestPath(Sema &S,
PathAccess = AS_public;
break;
case Sema::AR_dependent:
- return 0;
+ AnyDependent = true;
+ goto Next;
case Sema::AR_delayed:
llvm_unreachable("friend resolution is never delayed"); break;
}
@@ -272,9 +435,23 @@ static CXXBasePath *FindBestPath(Sema &S,
if (BestPath == 0 || PathAccess < BestPath->Access) {
BestPath = &*PI;
BestPath->Access = PathAccess;
+
+ // Short-circuit if we found a public path.
+ if (BestPath->Access == AS_public)
+ return BestPath;
}
+
+ Next: ;
}
+ assert((!BestPath || BestPath->Access != AS_public) &&
+ "fell out of loop with public path");
+
+ // We didn't find a public path, but at least one path was subject
+ // to dependent friendship, so delay the check.
+ if (AnyDependent)
+ return 0;
+
return BestPath;
}
@@ -402,7 +579,9 @@ static void DiagnoseBadAccess(Sema &S, SourceLocation Loc,
/// Try to elevate access using friend declarations. This is
/// potentially quite expensive.
-static void TryElevateAccess(Sema &S,
+///
+/// \return true if elevation was dependent
+static bool TryElevateAccess(Sema &S,
const EffectiveContext &EC,
const Sema::AccessedEntity &Entity,
AccessSpecifier &Access) {
@@ -424,14 +603,14 @@ static void TryElevateAccess(Sema &S,
switch (GetFriendKind(S, EC, DeclaringClass)) {
case Sema::AR_accessible: DeclAccess = AS_public; break;
case Sema::AR_inaccessible: break;
- case Sema::AR_dependent: /* FIXME: delay dependent friendship */ return;
+ case Sema::AR_dependent: return true;
case Sema::AR_delayed: llvm_unreachable("friend status is never delayed");
}
}
if (DeclaringClass == NamingClass) {
Access = DeclAccess;
- return;
+ return false;
}
}
@@ -441,16 +620,31 @@ static void TryElevateAccess(Sema &S,
CXXBasePaths Paths;
CXXBasePath *Path = FindBestPath(S, EC, Entity.getNamingClass(),
DeclaringClass, DeclAccess, Paths);
- if (!Path) {
- // FIXME: delay dependent friendship
- return;
- }
+ if (!Path)
+ return true;
// Grab the access along the best path (note that this includes the
// final-step access).
AccessSpecifier NewAccess = Path->Access;
assert(NewAccess <= Access && "access along best path worse than direct?");
Access = NewAccess;
+ return false;
+}
+
+static void DelayAccess(Sema &S,
+ const EffectiveContext &EC,
+ SourceLocation Loc,
+ const Sema::AccessedEntity &Entity) {
+ assert(EC.isDependent() && "delaying non-dependent access");
+ DeclContext *DC = EC.getPrimaryContext();
+ assert(DC->isDependentContext() && "delaying non-dependent access");
+ DependentDiagnostic::Create(S.Context, DC, DependentDiagnostic::Access,
+ Loc,
+ Entity.isMemberAccess(),
+ Entity.getAccess(),
+ Entity.getTargetDecl(),
+ Entity.getNamingClass(),
+ Entity.getDiag());
}
/// Checks access to an entity from the given effective context.
@@ -474,10 +668,13 @@ static Sema::AccessResult CheckEffectiveAccess(Sema &S,