[cfe-commits] r99708 - in /cfe/trunk: include/clang/AST/DeclBase.h lib/Sema/SemaDecl.cpp lib/Sema/SemaTemplateInstantiateDecl.cpp test/CXX/temp/temp.decls/temp.friend/p1.cpp

John McCall rjmccall at apple.com
Fri Mar 26 22:57:59 PDT 2010


Author: rjmccall
Date: Sat Mar 27 00:57:59 2010
New Revision: 99708

URL: http://llvm.org/viewvc/llvm-project?rev=99708&view=rev
Log:
Implement method friends in class templates and fix a few related problems.


Modified:
    cfe/trunk/include/clang/AST/DeclBase.h
    cfe/trunk/lib/Sema/SemaDecl.cpp
    cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp
    cfe/trunk/test/CXX/temp/temp.decls/temp.friend/p1.cpp

Modified: cfe/trunk/include/clang/AST/DeclBase.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DeclBase.h?rev=99708&r1=99707&r2=99708&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/DeclBase.h (original)
+++ cfe/trunk/include/clang/AST/DeclBase.h Sat Mar 27 00:57:59 2010
@@ -452,15 +452,23 @@
   /// same entity may not (and probably don't) share this property.
   void setObjectOfFriendDecl(bool PreviouslyDeclared) {
     unsigned OldNS = IdentifierNamespace;
-    assert((OldNS == IDNS_Tag || OldNS == IDNS_Ordinary ||
-            OldNS == (IDNS_Tag | IDNS_Ordinary))
-           && "unsupported namespace for undeclared friend");
-    if (!PreviouslyDeclared) IdentifierNamespace = 0;
+    assert((OldNS & (IDNS_Tag | IDNS_Ordinary |
+                     IDNS_TagFriend | IDNS_OrdinaryFriend)) &&
+           "namespace includes neither ordinary nor tag");
+    assert(!(OldNS & ~(IDNS_Tag | IDNS_Ordinary |
+                       IDNS_TagFriend | IDNS_OrdinaryFriend)) &&
+           "namespace includes other than ordinary or tag");
 
-    if (OldNS == IDNS_Tag)
+    IdentifierNamespace = 0;
+    if (OldNS & (IDNS_Tag | IDNS_TagFriend)) {
       IdentifierNamespace |= IDNS_TagFriend;
-    else
+      if (PreviouslyDeclared) IdentifierNamespace |= IDNS_Tag;
+    }
+
+    if (OldNS & (IDNS_Ordinary | IDNS_OrdinaryFriend)) {
       IdentifierNamespace |= IDNS_OrdinaryFriend;
+      if (PreviouslyDeclared) IdentifierNamespace |= IDNS_Ordinary;
+    }
   }
 
   enum FriendObjectKind {

Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=99708&r1=99707&r2=99708&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Sat Mar 27 00:57:59 2010
@@ -2998,13 +2998,13 @@
            "previously-undeclared friend function being created "
            "in a non-namespace context");
 
+    // For now, claim that the objects have no previous declaration.
     if (FunctionTemplate) {
-      FunctionTemplate->setObjectOfFriendDecl(
-                                   /* PreviouslyDeclared= */ !Previous.empty());
+      FunctionTemplate->setObjectOfFriendDecl(false);
       FunctionTemplate->setAccess(AS_public);
+    } else {
+      NewFD->setObjectOfFriendDecl(false);
     }
-    else
-      NewFD->setObjectOfFriendDecl(/* PreviouslyDeclared= */ !Previous.empty());
 
     NewFD->setAccess(AS_public);
   }
@@ -3154,6 +3154,17 @@
           Previous.getResultKind() != LookupResult::FoundOverloaded) &&
          "previous declaration set still overloaded");
 
+  if (isFriend && Redeclaration) {
+    AccessSpecifier Access = NewFD->getPreviousDeclaration()->getAccess();
+    if (FunctionTemplate) {
+      FunctionTemplate->setObjectOfFriendDecl(true);
+      FunctionTemplate->setAccess(Access);
+    } else {
+      NewFD->setObjectOfFriendDecl(true);
+    }
+    NewFD->setAccess(Access);
+  }
+
   // If we have a function template, check the template parameter
   // list. This will check and merge default template arguments.
   if (FunctionTemplate) {

Modified: cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp?rev=99708&r1=99707&r2=99708&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp Sat Mar 27 00:57:59 2010
@@ -503,13 +503,7 @@
     Decl *NewND;
 
     // Hack to make this work almost well pending a rewrite.
-    if (ND->getDeclContext()->isRecord()) {
-      // 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()) {
+    if (D->wasSpecialization()) {
       // Totally egregious hack to work around PR5866
       return 0;
     } else {
@@ -906,15 +900,8 @@
   // Check whether there is already a function template specialization for
   // this declaration.
   FunctionTemplateDecl *FunctionTemplate = D->getDescribedFunctionTemplate();
-
-  bool isFriend;
-  if (FunctionTemplate)
-    isFriend = (FunctionTemplate->getFriendObjectKind() != Decl::FOK_None);
-  else
-    isFriend = (D->getFriendObjectKind() != Decl::FOK_None);
-
   void *InsertPos = 0;
-  if (!isFriend && FunctionTemplate && !TemplateParams) {
+  if (FunctionTemplate && !TemplateParams) {
     llvm::FoldingSetNodeID ID;
     FunctionTemplateSpecializationInfo::Profile(ID,
                              TemplateArgs.getInnermost().getFlatArgumentList(),
@@ -930,6 +917,12 @@
       return Info->Function;
   }
 
+  bool isFriend;
+  if (FunctionTemplate)
+    isFriend = (FunctionTemplate->getFriendObjectKind() != Decl::FOK_None);
+  else
+    isFriend = (D->getFriendObjectKind() != Decl::FOK_None);
+
   bool MergeWithParentScope = (TemplateParams != 0) ||
     !(isa<Decl>(Owner) && 
       cast<Decl>(Owner)->isDefinedOutsideFunctionOrMethod());
@@ -1098,6 +1091,12 @@
       return Info->Function;
   }
 
+  bool isFriend;
+  if (FunctionTemplate)
+    isFriend = (FunctionTemplate->getFriendObjectKind() != Decl::FOK_None);
+  else
+    isFriend = (D->getFriendObjectKind() != Decl::FOK_None);
+
   bool MergeWithParentScope = (TemplateParams != 0) ||
     !(isa<Decl>(Owner) && 
       cast<Decl>(Owner)->isDefinedOutsideFunctionOrMethod());
@@ -1110,8 +1109,31 @@
     return 0;
   QualType T = TInfo->getType();
 
+  NestedNameSpecifier *Qualifier = D->getQualifier();
+  if (Qualifier) {
+    Qualifier = SemaRef.SubstNestedNameSpecifier(Qualifier,
+                                                 D->getQualifierRange(),
+                                                 TemplateArgs);
+    if (!Qualifier) return 0;
+  }
+
+  DeclContext *DC = Owner;
+  if (isFriend) {
+    if (Qualifier) {
+      CXXScopeSpec SS;
+      SS.setScopeRep(Qualifier);
+      SS.setRange(D->getQualifierRange());
+      DC = SemaRef.computeDeclContext(SS);
+    } else {
+      DC = SemaRef.FindInstantiatedContext(D->getLocation(),
+                                           D->getDeclContext(),
+                                           TemplateArgs);
+    }
+    if (!DC) return 0;
+  }
+
   // Build the instantiated method declaration.
-  CXXRecordDecl *Record = cast<CXXRecordDecl>(Owner);
+  CXXRecordDecl *Record = cast<CXXRecordDecl>(DC);
   CXXMethodDecl *Method = 0;
 
   DeclarationName Name = D->getDeclName();
@@ -1148,9 +1170,8 @@
                                    D->isStatic(), D->isInlineSpecified());
   }
 
-  // Substitute the nested name specifier, if any.
-  if (SubstQualifier(D, Method))
-    return 0;
+  if (Qualifier)
+    Method->setQualifierInfo(Qualifier, D->getQualifierRange());
 
   if (TemplateParams) {
     // Our resulting instantiation is actually a function template, since we
@@ -1170,7 +1191,10 @@
                                                     Method->getLocation(),
                                                     Method->getDeclName(),
                                                     TemplateParams, Method);
-    if (D->isOutOfLine())
+    if (isFriend) {
+      FunctionTemplate->setLexicalDeclContext(Owner);
+      FunctionTemplate->setObjectOfFriendDecl(true);
+    } else if (D->isOutOfLine())
       FunctionTemplate->setLexicalDeclContext(D->getLexicalDeclContext());
     Method->setDescribedFunctionTemplate(FunctionTemplate);
   } else if (FunctionTemplate) {
@@ -1178,7 +1202,7 @@
     Method->setFunctionTemplateSpecialization(FunctionTemplate,
                                               &TemplateArgs.getInnermost(),
                                               InsertPos);
-  } else {
+  } else if (!isFriend) {
     // Record that this is an instantiation of a member function.
     Method->setInstantiationOfMemberFunction(D, TSK_ImplicitInstantiation);
   }
@@ -1186,7 +1210,10 @@
   // If we are instantiating a member function defined
   // out-of-line, the instantiation will have the same lexical
   // context (which will be a namespace scope) as the template.
-  if (D->isOutOfLine())
+  if (isFriend) {
+    Method->setLexicalDeclContext(Owner);
+    Method->setObjectOfFriendDecl(true);
+  } else if (D->isOutOfLine())
     Method->setLexicalDeclContext(D->getLexicalDeclContext());
 
   // Attach the parameters
@@ -1200,8 +1227,8 @@
   LookupResult Previous(SemaRef, Name, SourceLocation(),
                         Sema::LookupOrdinaryName, Sema::ForRedeclaration);
 
-  if (!FunctionTemplate || TemplateParams) {
-    SemaRef.LookupQualifiedName(Previous, Owner);
+  if (!FunctionTemplate || TemplateParams || isFriend) {
+    SemaRef.LookupQualifiedName(Previous, Record);
 
     // In C++, the previous declaration we find might be a tag type
     // (class or enum). In this case, the new declaration will hide the
@@ -1221,9 +1248,19 @@
 
   Method->setAccess(D->getAccess());
 
-  if (!FunctionTemplate && (!Method->isInvalidDecl() || Previous.empty()) &&
-      !Method->getFriendObjectKind())
-    Owner->addDecl(Method);
+  if (FunctionTemplate) {
+    // If there's a function template, let our caller handle it.
+  } else if (Method->isInvalidDecl() && !Previous.empty()) {
+    // Don't hide a (potentially) valid declaration with an invalid one.
+  } else {
+    NamedDecl *DeclToAdd = (TemplateParams
+                            ? cast<NamedDecl>(FunctionTemplate)
+                            : Method);
+    if (isFriend)
+      Record->makeDeclVisibleInContext(DeclToAdd);
+    else
+      Owner->addDecl(DeclToAdd);
+  }
 
   return Method;
 }

Modified: cfe/trunk/test/CXX/temp/temp.decls/temp.friend/p1.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/temp/temp.decls/temp.friend/p1.cpp?rev=99708&r1=99707&r2=99708&view=diff
==============================================================================
--- cfe/trunk/test/CXX/temp/temp.decls/temp.friend/p1.cpp (original)
+++ cfe/trunk/test/CXX/temp/temp.decls/temp.friend/p1.cpp Sat Mar 27 00:57:59 2010
@@ -191,3 +191,28 @@
   }
   template void foo<int>();
 }
+
+namespace test9 {
+  template <class T> class A {
+    class B; class C;
+
+    int foo(B *b) {
+      return b->x;
+    }
+
+    int foo(C *c) {
+      return c->x; // expected-error {{'x' is a private member}}
+    }
+
+    class B {
+      int x;
+      friend int A::foo(B*);
+    };
+
+    class C {
+      int x; // expected-note {{declared private here}}
+    };
+  };
+
+  template class A<int>; // expected-note {{in instantiation}}
+}





More information about the cfe-commits mailing list