]> granicus.if.org Git - clang/commitdiff
Improve the internal representation and semantic analysis of friend
authorDouglas Gregor <dgregor@apple.com>
Tue, 13 Oct 2009 14:39:41 +0000 (14:39 +0000)
committerDouglas Gregor <dgregor@apple.com>
Tue, 13 Oct 2009 14:39:41 +0000 (14:39 +0000)
function templates.

This commit ensures that friend function templates are constructed as
FunctionTemplateDecls rather than partial FunctionDecls (as they
previously were). It then implements template instantiation for friend
function templates, injecting the friend function template only when
no previous declaration exists at the time of instantiation.

Oh, and make sure that explicit specialization declarations are not
friends.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@83970 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Basic/DiagnosticSemaKinds.td
lib/Sema/SemaDecl.cpp
lib/Sema/SemaDeclCXX.cpp
lib/Sema/SemaTemplate.cpp
lib/Sema/SemaTemplateInstantiateDecl.cpp
test/CXX/temp/temp.spec/temp.expl.spec/p20.cpp [new file with mode: 0644]
test/SemaTemplate/friend-template.cpp

index f15c028a538cb963bd77d8666a815431661ed2b0..051dd436bd02abfa49d6bd1bcb0f5942e6a78a59 100644 (file)
@@ -971,6 +971,8 @@ def err_specialization_after_instantiation : Error<
   "explicit specialization of %0 after instantiation">;
 def note_instantiation_required_here : Note<
   "%select{implicit|explicit}0 instantiation first required here">;
+def err_template_spec_friend : Error<
+  "template specialization declaration cannot be a friend">;
 
 // C++ class template specializations and out-of-line definitions
 def err_template_spec_needs_header : Error<
index 998abb34037fe08dde9398007e2ea0bf3ca0ccca..69426ee75548e7c0d8a92a927297551f573cb585 100644 (file)
@@ -2575,9 +2575,6 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
   // lexical context will be different from the semantic context.
   NewFD->setLexicalDeclContext(CurContext);
 
-  if (isFriend)
-    NewFD->setObjectOfFriendDecl(/* PreviouslyDeclared= */ PrevDecl != NULL);
-
   // Match up the template parameter lists with the scope specifier, then
   // determine whether we have a template or a template specialization.
   FunctionTemplateDecl *FunctionTemplate = 0;
@@ -2640,6 +2637,19 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
     }
   }
 
+  if (isFriend) {
+    if (FunctionTemplate) {
+      FunctionTemplate->setObjectOfFriendDecl(
+                                   /* PreviouslyDeclared= */ PrevDecl != NULL);
+      FunctionTemplate->setAccess(AS_public);
+    }
+    else
+      NewFD->setObjectOfFriendDecl(/* PreviouslyDeclared= */ PrevDecl != NULL);
+
+    NewFD->setAccess(AS_public);
+  }
+
+
   if (CXXMethodDecl *NewMD = dyn_cast<CXXMethodDecl>(NewFD)) {
     // Look for virtual methods in base classes that this method might override.
     CXXBasePaths Paths;
index eb1abafb7d02d36555a97df9c0e7f4cb52dca110..519694aea8462ff0d8d3905b68e13cf554145465 100644 (file)
@@ -4347,8 +4347,6 @@ Sema::ActOnFriendFunctionDecl(Scope *S,
                               Declarator &D,
                               bool IsDefinition,
                               MultiTemplateParamsArg TemplateParams) {
-  // FIXME: do something with template parameters
-
   const DeclSpec &DS = D.getDeclSpec();
 
   assert(DS.isFriendSpecified());
@@ -4404,6 +4402,7 @@ Sema::ActOnFriendFunctionDecl(Scope *S,
   // Recover from invalid scope qualifiers as if they just weren't there.
   NamedDecl *PrevDecl = 0;
   if (!ScopeQual.isInvalid() && ScopeQual.isSet()) {
+    // FIXME: RequireCompleteDeclContext
     DC = computeDeclContext(ScopeQual);
 
     // FIXME: handle dependent contexts
@@ -4479,7 +4478,7 @@ Sema::ActOnFriendFunctionDecl(Scope *S,
 
   bool Redeclaration = false;
   NamedDecl *ND = ActOnFunctionDeclarator(S, D, DC, T, DInfo, PrevDecl,
-                                          MultiTemplateParamsArg(*this),
+                                          move(TemplateParams),
                                           IsDefinition,
                                           Redeclaration);
   if (!ND) return DeclPtrTy();
index 9d39ddbe68f40efb5bd1efe519d4041f55265581..74120498573b9a709f237d640ca85382b9783d3a 100644 (file)
@@ -2720,7 +2720,16 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec,
         }
       }
     }
-  } else if (!TemplateParams && TUK != TUK_Friend) {
+  } else if (TemplateParams) {
+    if (TUK == TUK_Friend)
+      Diag(KWLoc, diag::err_template_spec_friend)
+        << CodeModificationHint::CreateRemoval(
+                                SourceRange(TemplateParams->getTemplateLoc(),
+                                            TemplateParams->getRAngleLoc()))
+        << SourceRange(LAngleLoc, RAngleLoc);
+    else
+      isExplicitSpecialization = true;
+  } else if (TUK != TUK_Friend) {
     Diag(KWLoc, diag::err_template_spec_needs_header)
       << CodeModificationHint::CreateInsertion(KWLoc, "template<> ");
     isExplicitSpecialization = true;
index 4fd2a734b1f022eeaf08063695f98fa3e0d44466..b03734346c4bc9014a74d540defdfda66b2c088e 100644 (file)
@@ -47,7 +47,8 @@ namespace {
     Decl *VisitEnumDecl(EnumDecl *D);
     Decl *VisitEnumConstantDecl(EnumConstantDecl *D);
     Decl *VisitFriendDecl(FriendDecl *D);
-    Decl *VisitFunctionDecl(FunctionDecl *D);
+    Decl *VisitFunctionDecl(FunctionDecl *D,
+                            TemplateParameterList *TemplateParams = 0);
     Decl *VisitCXXRecordDecl(CXXRecordDecl *D);
     Decl *VisitCXXMethodDecl(CXXMethodDecl *D,
                              TemplateParameterList *TemplateParams = 0);
@@ -280,6 +281,9 @@ Decl *TemplateDeclInstantiator::VisitFriendDecl(FriendDecl *D) {
     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 = Visit(ND);
     if (!NewND) return 0;
 
@@ -423,24 +427,31 @@ TemplateDeclInstantiator::VisitFunctionTemplateDecl(FunctionTemplateDecl *D) {
   if (!InstParams)
     return NULL;
 
-  // FIXME: Handle instantiation of nested function templates that aren't
-  // member function templates. This could happen inside a FriendDecl.
-  assert(isa<CXXMethodDecl>(D->getTemplatedDecl()));
-  CXXMethodDecl *InstMethod
-    = cast_or_null<CXXMethodDecl>(
-                 VisitCXXMethodDecl(cast<CXXMethodDecl>(D->getTemplatedDecl()),
-                                    InstParams));
-  if (!InstMethod)
+  FunctionDecl *Instantiated = 0;
+  if (CXXMethodDecl *DMethod = dyn_cast<CXXMethodDecl>(D->getTemplatedDecl()))
+    Instantiated = cast_or_null<FunctionDecl>(VisitCXXMethodDecl(DMethod, 
+                                                                 InstParams));
+  else
+    Instantiated = cast_or_null<FunctionDecl>(VisitFunctionDecl(
+                                                          D->getTemplatedDecl(), 
+                                                                InstParams));
+  
+  if (!Instantiated)
     return 0;
 
   // Link the instantiated function template declaration to the function
   // template from which it was instantiated.
   FunctionTemplateDecl *InstTemplate 
-    = InstMethod->getDescribedFunctionTemplate();
+    = Instantiated->getDescribedFunctionTemplate();
   InstTemplate->setAccess(D->getAccess());
-  assert(InstTemplate && "VisitCXXMethodDecl didn't create a template!");
-  InstTemplate->setInstantiatedFromMemberTemplate(D);
-  Owner->addDecl(InstTemplate);
+  assert(InstTemplate && 
+         "VisitFunctionDecl/CXXMethodDecl didn't create a template!");
+  if (!InstTemplate->getInstantiatedFromMemberTemplate())
+    InstTemplate->setInstantiatedFromMemberTemplate(D);
+  
+  // Add non-friends into the owner.
+  if (!InstTemplate->getFriendObjectKind())
+    Owner->addDecl(InstTemplate);
   return InstTemplate;
 }
 
@@ -478,12 +489,13 @@ Decl *TemplateDeclInstantiator::VisitCXXRecordDecl(CXXRecordDecl *D) {
 ///   1) instantiating function templates
 ///   2) substituting friend declarations
 /// FIXME: preserve function definitions in case #2
-Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D) {
+  Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D,
+                                       TemplateParameterList *TemplateParams) {
   // Check whether there is already a function template specialization for
   // this declaration.
   FunctionTemplateDecl *FunctionTemplate = D->getDescribedFunctionTemplate();
   void *InsertPos = 0;
-  if (FunctionTemplate) {
+  if (FunctionTemplate && !TemplateParams) {
     llvm::FoldingSetNodeID ID;
     FunctionTemplateSpecializationInfo::Profile(ID,
                              TemplateArgs.getInnermost().getFlatArgumentList(),
@@ -521,27 +533,79 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D) {
     Params[P]->setOwningFunction(Function);
   Function->setParams(SemaRef.Context, Params.data(), Params.size());
 
-  // If the original function was part of a friend declaration,
-  // inherit its namespace state and add it to the owner.
-  if (Decl::FriendObjectKind FOK = D->getFriendObjectKind()) {
-    bool WasDeclared = (FOK == Decl::FOK_Declared);
-    Function->setObjectOfFriendDecl(WasDeclared);
-    if (!Owner->isDependentContext())
-      DC->makeDeclVisibleInContext(Function, /* Recoverable = */ false);
-
-    Function->setInstantiationOfMemberFunction(D, TSK_ImplicitInstantiation);
+  if (TemplateParams) {
+    // Our resulting instantiation is actually a function template, since we
+    // are substituting only the outer template parameters. For example, given
+    //
+    //   template<typename T>
+    //   struct X {
+    //     template<typename U> friend void f(T, U);
+    //   };
+    //
+    //   X<int> x;
+    //
+    // We are instantiating the friend function template "f" within X<int>, 
+    // which means substituting int for T, but leaving "f" as a friend function
+    // template.
+    // Build the function template itself.
+    FunctionTemplate = FunctionTemplateDecl::Create(SemaRef.Context, Owner,
+                                                    Function->getLocation(),
+                                                    Function->getDeclName(),
+                                                    TemplateParams, Function);
+    Function->setDescribedFunctionTemplate(FunctionTemplate);
+    FunctionTemplate->setLexicalDeclContext(D->getLexicalDeclContext());
   }
-
+    
   if (InitFunctionInstantiation(Function, D))
     Function->setInvalidDecl();
 
   bool Redeclaration = false;
   bool OverloadableAttrRequired = false;
+    
   NamedDecl *PrevDecl = 0;
+  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.
+    Sema::LookupResult R;
+    SemaRef.LookupQualifiedName(R, DC, Function->getDeclName(), 
+                              Sema::LookupOrdinaryName, true);
+    
+    PrevDecl = R.getAsSingleDecl(SemaRef.Context);
+
+    // In C++, the previous declaration we find might be a tag type
+    // (class or enum). In this case, the new declaration will hide the
+    // tag type. Note that this does does not apply if we're declaring a
+    // typedef (C++ [dcl.typedef]p4).
+    if (PrevDecl && PrevDecl->getIdentifierNamespace() == Decl::IDNS_Tag)
+      PrevDecl = 0;
+  }
+  
   SemaRef.CheckFunctionDeclaration(Function, PrevDecl, Redeclaration,
                                    /*FIXME:*/OverloadableAttrRequired);
 
-  if (FunctionTemplate) {
+  // If the original function was part of a friend declaration,
+  // inherit its namespace state and add it to the owner.
+  NamedDecl *FromFriendD 
+      = TemplateParams? cast<NamedDecl>(D->getDescribedFunctionTemplate()) : D;
+  if (FromFriendD->getFriendObjectKind()) {
+    NamedDecl *ToFriendD = 0;
+    if (TemplateParams) {
+      ToFriendD = cast<NamedDecl>(FunctionTemplate);
+      PrevDecl = FunctionTemplate->getPreviousDeclaration();
+    } else {
+      ToFriendD = Function;
+      PrevDecl = Function->getPreviousDeclaration();
+    }
+    ToFriendD->setObjectOfFriendDecl(PrevDecl != NULL);
+    if (!Owner->isDependentContext() && !PrevDecl)
+      DC->makeDeclVisibleInContext(ToFriendD, /* Recoverable = */ false);
+
+    if (!TemplateParams)
+      Function->setInstantiationOfMemberFunction(D, TSK_ImplicitInstantiation);
+  }
+
+  if (FunctionTemplate && !TemplateParams) {
     // Record this function template specialization.
     Function->setFunctionTemplateSpecialization(SemaRef.Context,
                                                 FunctionTemplate,
@@ -687,7 +751,8 @@ TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D,
   SemaRef.CheckFunctionDeclaration(Method, PrevDecl, Redeclaration,
                                    /*FIXME:*/OverloadableAttrRequired);
 
-  if (!FunctionTemplate && (!Method->isInvalidDecl() || !PrevDecl))
+  if (!FunctionTemplate && (!Method->isInvalidDecl() || !PrevDecl) &&
+      !Method->getFriendObjectKind())
     Owner->addDecl(Method);
 
   return Method;
diff --git a/test/CXX/temp/temp.spec/temp.expl.spec/p20.cpp b/test/CXX/temp/temp.spec/temp.expl.spec/p20.cpp
new file mode 100644 (file)
index 0000000..d270b81
--- /dev/null
@@ -0,0 +1,14 @@
+// RUN: clang-cc -fsyntax-only -verify %s
+template<typename T>
+void f(T);
+
+template<typename T>
+struct A { };
+
+struct X {
+  template<> friend void f<int>(int); // expected-error{{in class scope}}
+  template<> friend class A<int>; // expected-error{{cannot be a friend}}
+  
+  friend void f<float>(float); // okay
+  friend class A<float>; // okay
+};
index 9a483aeb5b1e8ba9b2fc6147ad7bbe8fe0fb2106..761c13076d2ab03a739c152d33591f49f252b11a 100644 (file)
@@ -44,3 +44,21 @@ template<>
 struct X0<int> {
   template<typename U> friend struct X0;
 };
+
+template<typename T>
+struct X1 {
+  template<typename U> friend void f2(U);
+  template<typename U> friend void f3(U);
+};
+
+template<typename U> void f2(U);
+
+X1<int> x1i;
+
+template<> void f2(int);
+
+// FIXME: Should this declaration of f3 be required for the specialization of
+// f3<int> (further below) to work? GCC and EDG don't require it, we do...
+template<typename U> void f3(U);
+
+template<> void f3(int);