Properly instantiate friend class template declarations and link them into
the redeclaration chain.  Recommitted from r99477 with a fix:  we need to
merge in default template arguments from previous declarations.



git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@99496 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Sema/SemaAccess.cpp b/lib/Sema/SemaAccess.cpp
index 7e2520c..62b13d4 100644
--- a/lib/Sema/SemaAccess.cpp
+++ b/lib/Sema/SemaAccess.cpp
@@ -240,13 +240,13 @@
                                         ClassTemplateDecl *Friend) {
   Sema::AccessResult OnFailure = Sema::AR_inaccessible;
 
+  // Check whether the friend is the template of a class in the
+  // context chain.
   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:
+    // Figure out whether the current class has a template:
     ClassTemplateDecl *CTD;
 
     // A specialization of the template...
@@ -264,6 +264,10 @@
     if (Friend == CTD->getCanonicalDecl())
       return Sema::AR_accessible;
 
+    // If the context isn't dependent, it can't be a dependent match.
+    if (!EC.isDependent())
+      continue;
+
     // 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())
diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 7b0e88d..de6fff7 100644
--- a/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -500,21 +500,17 @@
 
     // Hack to make this work almost well pending a rewrite.
     if (ND->getDeclContext()->isRecord()) {
-      if (!ND->getDeclContext()->isDependentContext()) {
-        NewND = SemaRef.FindInstantiatedDecl(D->getLocation(), ND, 
-                                             TemplateArgs);
-      } else {
-        // FIXME: Hack to avoid crashing when incorrectly trying to instantiate
-        // templated friend declarations. This doesn't produce a correct AST;
-        // however this is sufficient for some AST analysis. The real solution
-        // must be put in place during the pending rewrite. See PR5848.
-        return 0;
-      }
+      // FIXME: Hack to avoid crashing when incorrectly trying to instantiate
+      // templated friend declarations. This doesn't produce a correct AST;
+      // however this is sufficient for some AST analysis. The real solution
+      // must be put in place during the pending rewrite. See PR5848.
+      return 0;
     } else if (D->wasSpecialization()) {
       // Totally egregious hack to work around PR5866
       return 0;
-    } else
+    } else {
       NewND = Visit(ND);
+    }
     if (!NewND) return 0;
 
     FU = cast<NamedDecl>(NewND);
@@ -641,6 +637,8 @@
 }
 
 Decl *TemplateDeclInstantiator::VisitClassTemplateDecl(ClassTemplateDecl *D) {
+  bool isFriend = (D->getFriendObjectKind() != Decl::FOK_None);
+
   // Create a local instantiation scope for this class template, which
   // will contain the instantiations of the template parameters.
   Sema::LocalInstantiationScope Scope(SemaRef);
@@ -650,32 +648,105 @@
     return NULL;
 
   CXXRecordDecl *Pattern = D->getTemplatedDecl();
+
+  // Instantiate the qualifier.  We have to do this first in case
+  // we're a friend declaration, because if we are then we need to put
+  // the new declaration in the appropriate context.
+  NestedNameSpecifier *Qualifier = Pattern->getQualifier();
+  if (Qualifier) {
+    Qualifier = SemaRef.SubstNestedNameSpecifier(Qualifier,
+                                                 Pattern->getQualifierRange(),
+                                                 TemplateArgs);
+    if (!Qualifier) return 0;
+  }
+
+  CXXRecordDecl *PrevDecl = 0;
+  ClassTemplateDecl *PrevClassTemplate = 0;
+
+  // If this isn't a friend, then it's a member template, in which
+  // case we just want to build the instantiation in the
+  // specialization.  If it is a friend, we want to build it in
+  // the appropriate context.
+  DeclContext *DC = Owner;
+  if (isFriend) {
+    if (Qualifier) {
+      CXXScopeSpec SS;
+      SS.setScopeRep(Qualifier);
+      SS.setRange(Pattern->getQualifierRange());
+      DC = SemaRef.computeDeclContext(SS);
+      if (!DC) return 0;
+    } else {
+      DC = SemaRef.FindInstantiatedContext(Pattern->getLocation(),
+                                           Pattern->getDeclContext(),
+                                           TemplateArgs);
+    }
+
+    // Look for a previous declaration of the template in the owning
+    // context.
+    LookupResult R(SemaRef, Pattern->getDeclName(), Pattern->getLocation(),
+                   Sema::LookupOrdinaryName, Sema::ForRedeclaration);
+    SemaRef.LookupQualifiedName(R, DC);
+
+    if (R.isSingleResult()) {
+      PrevClassTemplate = R.getAsSingle<ClassTemplateDecl>();
+      if (PrevClassTemplate)
+        PrevDecl = PrevClassTemplate->getTemplatedDecl();
+    }
+
+    if (!PrevClassTemplate && Qualifier) {
+      SemaRef.Diag(Pattern->getLocation(), diag::err_not_tag_in_scope)
+        << Pattern->getDeclName() << Pattern->getQualifierRange();
+      return 0;
+    }
+
+    if (PrevClassTemplate) {
+      TemplateParameterList *PrevParams
+        = PrevClassTemplate->getTemplateParameters();
+
+      // Make sure the parameter lists match.
+      if (!SemaRef.TemplateParameterListsAreEqual(InstParams, PrevParams,
+                                                  /*Complain=*/true,
+                                                  Sema::TPL_TemplateMatch))
+        return 0;
+
+      // Do some additional validation, then merge default arguments
+      // from the existing declarations.
+      if (SemaRef.CheckTemplateParameterList(InstParams, PrevParams,
+                                             Sema::TPC_ClassTemplate))
+        return 0;
+    }
+  }
+
   CXXRecordDecl *RecordInst
-    = CXXRecordDecl::Create(SemaRef.Context, Pattern->getTagKind(), Owner,
+    = CXXRecordDecl::Create(SemaRef.Context, Pattern->getTagKind(), DC,
                             Pattern->getLocation(), Pattern->getIdentifier(),
-                            Pattern->getTagKeywordLoc(), /*PrevDecl=*/ NULL,
+                            Pattern->getTagKeywordLoc(), PrevDecl,
                             /*DelayTypeCreation=*/true);
 
-  // Substitute the nested name specifier, if any.
-  if (SubstQualifier(Pattern, RecordInst))
-    return 0;
+  if (Qualifier)
+    RecordInst->setQualifierInfo(Qualifier, Pattern->getQualifierRange());
 
   ClassTemplateDecl *Inst
-    = ClassTemplateDecl::Create(SemaRef.Context, Owner, D->getLocation(),
-                                D->getIdentifier(), InstParams, RecordInst, 0);
+    = ClassTemplateDecl::Create(SemaRef.Context, DC, D->getLocation(),
+                                D->getIdentifier(), InstParams, RecordInst,
+                                PrevClassTemplate);
   RecordInst->setDescribedClassTemplate(Inst);
-  if (D->getFriendObjectKind())
-    Inst->setObjectOfFriendDecl(true);
-  else
+  if (isFriend) {
+    Inst->setObjectOfFriendDecl(PrevClassTemplate != 0);
+    // TODO: do we want to track the instantiation progeny of this
+    // friend target decl?
+  } else {
     Inst->setAccess(D->getAccess());
-  Inst->setInstantiatedFromMemberTemplate(D);
+    Inst->setInstantiatedFromMemberTemplate(D);
+  }
   
   // Trigger creation of the type for the instantiation.
   SemaRef.Context.getInjectedClassNameType(RecordInst,
                   Inst->getInjectedClassNameSpecialization(SemaRef.Context));
   
   // Finish handling of friends.
-  if (Inst->getFriendObjectKind()) {
+  if (isFriend) {
+    DC->makeDeclVisibleInContext(Inst, /*Recoverable*/ false);
     return Inst;
   }
   
diff --git a/test/CXX/temp/temp.decls/temp.friend/p1.cpp b/test/CXX/temp/temp.decls/temp.friend/p1.cpp
index 277106c..9d55896 100644
--- a/test/CXX/temp/temp.decls/temp.friend/p1.cpp
+++ b/test/CXX/temp/temp.decls/temp.friend/p1.cpp
@@ -114,7 +114,30 @@
 
   template class User<bool>;
   template class User<int>; // expected-note {{requested here}}
+}
 
+namespace test4 {
+  template <class T> class A {
+    template <class T0> friend class B;
+    bool foo(const A<T> *) const;
+  };
+
+  template <class T> class B {
+    bool bar(const A<T> *a, const A<T> *b) {
+      return a->foo(b);
+    }
+  };
+
+  template class B<int>;
+}
+
+namespace test5 {
+  template <class T, class U=int> class A {};
+  template <class T> class B {
+    template <class X, class Y> friend class A;
+  };
+  template class B<int>;
+  template class A<int>;
 }
 
 namespace Dependent {
diff --git a/test/SemaTemplate/friend-template.cpp b/test/SemaTemplate/friend-template.cpp
index 8bc2631..6ee30aa 100644
--- a/test/SemaTemplate/friend-template.cpp
+++ b/test/SemaTemplate/friend-template.cpp
@@ -74,12 +74,16 @@
   template<typename T>
   class X3 {
     template<typename U, U Value> friend struct X2a;
-    template<typename U, T Value> friend struct X2b;
+
+    // FIXME: the redeclaration note ends up here because redeclaration
+    // lookup ends up finding the friend target from X3<int>.
+    template<typename U, T Value> friend struct X2b; // expected-error {{template non-type parameter has a different type 'long' in template redeclaration}} \
+      // expected-note {{previous non-type template parameter with type 'int' is here}}
   };
 
   X3<int> x3i; // okay
 
-  X3<long> x3l; // FIXME: should cause an instantiation-time failure
+  X3<long> x3l; // expected-note {{in instantiation}}
 }
 
 // PR5716