Implement a framework for the delay of arbitrary diagnostics within
templates.  So delay access-control diagnostics when (for example) the target
of a friend declaration is a specific specialization of a template.

I was surprised to find that this was required for an access-controlled selfhost.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@99383 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp
index fcc3e3d..7f2e35b 100644
--- a/lib/AST/ASTContext.cpp
+++ b/lib/AST/ASTContext.cpp
@@ -45,7 +45,8 @@
   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 1aac7cf..9e92855 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 @@
   // 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 @@
     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 @@
   // 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 @@
   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 @@
       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 @@
   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 @@
 // 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 23c74a8..85fa4f1 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -2648,9 +2648,13 @@
                                     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 40b320c..e74c8f6 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 @@
 
 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 @@
     }
   }
 
+  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,86 +107,232 @@
   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()));
+                                        CanQualType Friend) {
+  if (const RecordType *RT = Friend->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;
+  // TODO: we can do better than this
+  if (Friend->isDependentType())
+    return Sema::AR_dependent;
 
-    return Sema::AR_inaccessible;
+  return Sema::AR_inaccessible;
+}
+
+/// 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 (llvm::SmallVectorImpl<CXXRecordDecl*>::const_iterator
+         I = EC.Records.begin(), E = EC.Records.end(); I != E; ++I) {
+    CXXRecordDecl *Record = *I;
+
+    // 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;
+
+    // A specialization of the template...
+    if (isa<ClassTemplateSpecializationDecl>(Record)) {
+      CTD = cast<ClassTemplateSpecializationDecl>(Record)
+        ->getSpecializedTemplate();
+
+    // ... or the template pattern itself.
+    } else {
+      CTD = Record->getDescribedClassTemplate();
+      if (!CTD) continue;
+    }
+
+    // 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;
   }
 
-  NamedDecl *D
-    = cast<NamedDecl>(Friend->getFriendDecl()->getCanonicalDecl());
+  return OnFailure;
+}
+
+/// 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 (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;
+
+  if (Friend == FTD->getCanonicalDecl())
+    return Sema::AR_accessible;
+
+  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.
 
-  // 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;
+  if (isa<ClassTemplateDecl>(Friend))
+    return MatchesFriend(S, EC, cast<ClassTemplateDecl>(Friend));
 
-      // A specialization of the template...
-      if (isa<ClassTemplateSpecializationDecl>(Record)) {
-        CTD = cast<ClassTemplateSpecializationDecl>(Record)
-                ->getSpecializedTemplate();
+  if (isa<FunctionTemplateDecl>(Friend))
+    return MatchesFriend(S, EC, cast<FunctionTemplateDecl>(Friend));
 
-      // ... or the template pattern itself.
-      } else {
-        CTD = Record->getDescribedClassTemplate();
-      }
+  if (isa<CXXRecordDecl>(Friend))
+    return MatchesFriend(S, EC, cast<CXXRecordDecl>(Friend));
 
-      if (CTD && D == CTD->getCanonicalDecl())
-        return Sema::AR_accessible;
-    }
-
-    return Sema::AR_inaccessible;
-  }
-
-  // Same thing for function templates.
-  if (isa<FunctionTemplateDecl>(D)) {
-    if (!EC.Function) return Sema::AR_inaccessible;
-
-    FunctionTemplateDecl *FTD = EC.Function->getPrimaryTemplate();
-    if (!FTD)
-      FTD = EC.Function->getDescribedFunctionTemplate();
-
-    if (FTD && D == FTD->getCanonicalDecl())
-      return Sema::AR_accessible;
-      
-    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)
-    return Sema::AR_accessible;
-
-  if (isa<CXXRecordDecl>(D))
-    return MatchesFriend(S, EC, cast<CXXRecordDecl>(D));
-
-  return Sema::AR_inaccessible;
+  assert(isa<FunctionDecl>(Friend) && "unknown friend decl kind");
+  return MatchesFriend(S, EC, cast<FunctionDecl>(Friend));
 }
 
 static Sema::AccessResult GetFriendKind(Sema &S,
@@ -230,6 +390,8 @@
 
   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 @@
           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 @@
     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 @@
 
 /// 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 @@
       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 @@
   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 @@
     return Sema::AR_accessible;
 
   // Try to elevate access.
-  // FIXME: delay if elevation was dependent?
   // TODO: on some code, it might be better to do the protected check
   // without trying to elevate first.
-  TryElevateAccess(S, EC, Entity, Access);
+  if (TryElevateAccess(S, EC, Entity, Access)) {
+    DelayAccess(S, EC, Loc, Entity);
+    return Sema::AR_dependent;
+  }
+
   if (Access == AS_public) return Sema::AR_accessible;
 
   // Protected access.
@@ -526,6 +723,35 @@
     DD.Triggered = true;
 }
 
+void Sema::HandleDependentAccessCheck(const DependentDiagnostic &DD,
+                        const MultiLevelTemplateArgumentList &TemplateArgs) {
+  SourceLocation Loc = DD.getAccessLoc();
+  AccessSpecifier Access = DD.getAccess();
+
+  Decl *NamingD = FindInstantiatedDecl(Loc, DD.getAccessNamingClass(),
+                                       TemplateArgs);
+  if (!NamingD) return;
+  Decl *TargetD = FindInstantiatedDecl(Loc, DD.getAccessTarget(),
+                                       TemplateArgs);
+  if (!TargetD) return;
+
+  if (DD.isAccessToMember()) {
+    AccessedEntity Entity(AccessedEntity::Member,
+                          cast<CXXRecordDecl>(NamingD),
+                          Access,
+                          cast<NamedDecl>(TargetD));
+    Entity.setDiag(DD.getDiagnostic());
+    CheckAccess(*this, Loc, Entity);
+  } else {
+    AccessedEntity Entity(AccessedEntity::Base,
+                          cast<CXXRecordDecl>(TargetD),
+                          cast<CXXRecordDecl>(NamingD),
+                          Access);
+    Entity.setDiag(DD.getDiagnostic());
+    CheckAccess(*this, Loc, Entity);
+  }
+}
+
 Sema::AccessResult Sema::CheckUnresolvedLookupAccess(UnresolvedLookupExpr *E,
                                                      DeclAccessPair Found) {
   if (!getLangOptions().AccessControl ||
diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp
index dbe041c..15a7946 100644
--- a/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -15,6 +15,7 @@
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/DeclVisitor.h"
+#include "clang/AST/DependentDiagnostic.h"
 #include "clang/AST/Expr.h"
 #include "clang/AST/ExprCXX.h"
 #include "clang/AST/TypeLoc.h"
@@ -1839,6 +1840,8 @@
   ActOnFinishFunctionBody(DeclPtrTy::make(Function), move(Body),
                           /*IsInstantiation=*/true);
 
+  PerformDependentDiagnostics(PatternDecl, TemplateArgs);
+
   CurContext = PreviousContext;
 
   DeclGroupRef DG(Function);
@@ -2475,3 +2478,17 @@
     InstantiateStaticDataMemberDefinition(/*FIXME:*/Inst.second, Var, true);
   }
 }
+
+void Sema::PerformDependentDiagnostics(const DeclContext *Pattern,
+                       const MultiLevelTemplateArgumentList &TemplateArgs) {
+  for (DeclContext::ddiag_iterator I = Pattern->ddiag_begin(),
+         E = Pattern->ddiag_end(); I != E; ++I) {
+    DependentDiagnostic *DD = *I;
+
+    switch (DD->getKind()) {
+    case DependentDiagnostic::Access:
+      HandleDependentAccessCheck(*DD, TemplateArgs);
+      break;
+    }
+  }
+}