Implement dependent friend function template specializations.



git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@100753 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h
index 45574b9..dc669a9 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -2839,6 +2839,10 @@
                                          SourceLocation PrevPointOfInstantiation,
                                          bool &SuppressNew);
     
+  bool CheckDependentFunctionTemplateSpecialization(FunctionDecl *FD,
+                    const TemplateArgumentListInfo &ExplicitTemplateArgs,
+                                                    LookupResult &Previous);
+                                                    
   bool CheckFunctionTemplateSpecialization(FunctionDecl *FD,
                         const TemplateArgumentListInfo *ExplicitTemplateArgs,
                                            LookupResult &Previous);
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index 590a014..8df611f 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -2935,7 +2935,7 @@
       // This is a function template specialization.
       isFunctionTemplateSpecialization = true;
 
-      // C++0x [temp.expl.spec]p20 forbids "template<> void foo(int);".
+      // C++0x [temp.expl.spec]p20 forbids "template<> friend void foo(int);".
       if (isFriend && isFunctionTemplateSpecialization) {
         // We want to remove the "template<>", found here.
         SourceRange RemoveRange = TemplateParams->getSourceRange();
@@ -3139,23 +3139,38 @@
       // "friend void foo<>(int);" is an implicit specialization decl.
       isFunctionTemplateSpecialization = true;
     }
+  } else if (isFriend && isFunctionTemplateSpecialization) {
+    // This combination is only possible in a recovery case;  the user
+    // wrote something like:
+    //   template <> friend void foo(int);
+    // which we're recovering from as if the user had written:
+    //   friend void foo<>(int);
+    // Go ahead and fake up a template id.
+    HasExplicitTemplateArgs = true;
+    TemplateArgs.setLAngleLoc(D.getIdentifierLoc());
+    TemplateArgs.setRAngleLoc(D.getIdentifierLoc());
   }
 
-  if (isFunctionTemplateSpecialization) {
-    if (isFriend && NewFD->getType()->isDependentType()) {
-      // FIXME: When we see a friend of a function template
-      // specialization with a dependent type, we can't match it now;
-      // for now, we just drop it, until we have a reasonable way to
-      // represent the parsed-but-not-matched friend function template
-      // specialization in the AST.
-      return 0;
-    } else if (CheckFunctionTemplateSpecialization(NewFD,
-                                   (HasExplicitTemplateArgs ? &TemplateArgs : 0),
-                                                 Previous))
+  // If it's a friend (and only if it's a friend), it's possible
+  // that either the specialized function type or the specialized
+  // template is dependent, and therefore matching will fail.  In
+  // this case, don't check the specialization yet.
+  if (isFunctionTemplateSpecialization && isFriend &&
+      (NewFD->getType()->isDependentType() || DC->isDependentContext())) {
+    assert(HasExplicitTemplateArgs &&
+           "friend function specialization without template args");
+    if (CheckDependentFunctionTemplateSpecialization(NewFD, TemplateArgs,
+                                                     Previous))
       NewFD->setInvalidDecl();
-  } else if (isExplicitSpecialization && isa<CXXMethodDecl>(NewFD) &&
-             CheckMemberSpecialization(NewFD, Previous))
-    NewFD->setInvalidDecl();
+  } else if (isFunctionTemplateSpecialization) {
+    if (CheckFunctionTemplateSpecialization(NewFD,
+                               (HasExplicitTemplateArgs ? &TemplateArgs : 0),
+                                            Previous))
+      NewFD->setInvalidDecl();
+  } else if (isExplicitSpecialization && isa<CXXMethodDecl>(NewFD)) {
+    if (CheckMemberSpecialization(NewFD, Previous))
+      NewFD->setInvalidDecl();
+  }
     
   // Perform semantic checking on the function declaration.
   bool OverloadableAttrRequired = false; // FIXME: HACK!
diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp
index 298ce06..7cdeea4 100644
--- a/lib/Sema/SemaDeclCXX.cpp
+++ b/lib/Sema/SemaDeclCXX.cpp
@@ -5638,9 +5638,6 @@
   FrD->setAccess(AS_public);
   CurContext->addDecl(FrD);
 
-  if (D.getName().getKind() == UnqualifiedId::IK_TemplateId)
-    FrD->setSpecialization(true);
-
   return DeclPtrTy::make(ND);
 }
 
diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp
index 44afbd7..584cc3e 100644
--- a/lib/Sema/SemaTemplate.cpp
+++ b/lib/Sema/SemaTemplate.cpp
@@ -4083,6 +4083,42 @@
   return false;
 }
 
+/// \brief Perform semantic analysis for the given dependent function
+/// template specialization.  The only possible way to get a dependent
+/// function template specialization is with a friend declaration,
+/// like so:
+///
+///   template <class T> void foo(T);
+///   template <class T> class A {
+///     friend void foo<>(T);
+///   };
+///
+/// There really isn't any useful analysis we can do here, so we
+/// just store the information.
+bool
+Sema::CheckDependentFunctionTemplateSpecialization(FunctionDecl *FD,
+                   const TemplateArgumentListInfo &ExplicitTemplateArgs,
+                                                   LookupResult &Previous) {
+  // Remove anything from Previous that isn't a function template in
+  // the correct context.
+  DeclContext *FDLookupContext = FD->getDeclContext()->getLookupContext();
+  LookupResult::Filter F = Previous.makeFilter();
+  while (F.hasNext()) {
+    NamedDecl *D = F.next()->getUnderlyingDecl();
+    if (!isa<FunctionTemplateDecl>(D) ||
+        !FDLookupContext->Equals(D->getDeclContext()->getLookupContext()))
+      F.erase();
+  }
+  F.done();
+
+  // Should this be diagnosed here?
+  if (Previous.empty()) return true;
+
+  FD->setDependentTemplateSpecialization(Context, Previous.asUnresolvedSet(),
+                                         ExplicitTemplateArgs);
+  return false;
+}
+
 /// \brief Perform semantic analysis for the given function template 
 /// specialization.
 ///
diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 69f183c..9df345a 100644
--- a/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -497,18 +497,11 @@
   NamedDecl *ND = D->getFriendDecl();
   assert(ND && "friend decl must be a decl or a type!");
 
-  // FIXME: We have a problem here, because the nested call to Visit(ND)
-  // will inject the thing that the friend references into the current
-  // owner, which is wrong.
-  Decl *NewND;
-
-  // Hack to make this work almost well pending a rewrite.
-  if (D->wasSpecialization()) {
-    // Totally egregious hack to work around PR5866
-    return 0;
-  } else {
-    NewND = Visit(ND);
-  }
+  // All of the Visit implementations for the various potential friend
+  // declarations have to be carefully written to work for friend
+  // objects, with the most important detail being that the target
+  // decl should almost certainly not be placed in Owner.
+  Decl *NewND = Visit(ND);
   if (!NewND) return 0;
 
   FriendDecl *FD =
@@ -1024,11 +1017,47 @@
 
   bool Redeclaration = false;
   bool OverloadableAttrRequired = false;
+  bool isExplicitSpecialization = false;
     
   LookupResult Previous(SemaRef, Function->getDeclName(), SourceLocation(),
                         Sema::LookupOrdinaryName, Sema::ForRedeclaration);
 
-  if (TemplateParams || !FunctionTemplate) {
+  if (DependentFunctionTemplateSpecializationInfo *Info
+        = D->getDependentSpecializationInfo()) {
+    assert(isFriend && "non-friend has dependent specialization info?");
+
+    // This needs to be set now for future sanity.
+    Function->setObjectOfFriendDecl(/*HasPrevious*/ true);
+
+    // Instantiate the explicit template arguments.
+    TemplateArgumentListInfo ExplicitArgs(Info->getLAngleLoc(),
+                                          Info->getRAngleLoc());
+    for (unsigned I = 0, E = Info->getNumTemplateArgs(); I != E; ++I) {
+      TemplateArgumentLoc Loc;
+      if (SemaRef.Subst(Info->getTemplateArg(I), Loc, TemplateArgs))
+        return 0;
+
+      ExplicitArgs.addArgument(Loc);
+    }
+
+    // Map the candidate templates to their instantiations.
+    for (unsigned I = 0, E = Info->getNumTemplates(); I != E; ++I) {
+      Decl *Temp = SemaRef.FindInstantiatedDecl(D->getLocation(),
+                                                Info->getTemplate(I),
+                                                TemplateArgs);
+      if (!Temp) return 0;
+
+      Previous.addDecl(cast<FunctionTemplateDecl>(Temp));
+    }
+
+    if (SemaRef.CheckFunctionTemplateSpecialization(Function,
+                                                    &ExplicitArgs,
+                                                    Previous))
+      Function->setInvalidDecl();
+                                          
+    isExplicitSpecialization = true;
+
+  } else if (TemplateParams || !FunctionTemplate) {
     // Look only into the namespace where the friend would be declared to 
     // find a previous declaration. This is the innermost enclosing namespace, 
     // as described in ActOnFriendFunctionDecl.
@@ -1043,7 +1072,7 @@
   }
   
   SemaRef.CheckFunctionDeclaration(/*Scope*/ 0, Function, Previous,
-                                   false, Redeclaration,
+                                   isExplicitSpecialization, Redeclaration,
                                    /*FIXME:*/OverloadableAttrRequired);
 
   // If the original function was part of a friend declaration,