[cfe-commits] r78274 - in /cfe/trunk: include/clang/Basic/ include/clang/Parse/ lib/Parse/ lib/Sema/ test/CXX/basic/basic.lookup/basic.lookup.unqual/ test/CXX/class/class.friend/ test/Parser/

John McCall rjmccall at apple.com
Wed Aug 5 19:15:44 PDT 2009


Author: rjmccall
Date: Wed Aug  5 21:15:43 2009
New Revision: 78274

URL: http://llvm.org/viewvc/llvm-project?rev=78274&view=rev
Log:
First pass at friend semantics.


Added:
    cfe/trunk/test/CXX/class/class.friend/
    cfe/trunk/test/CXX/class/class.friend/p1.cpp
    cfe/trunk/test/CXX/class/class.friend/p2.cpp
    cfe/trunk/test/CXX/class/class.friend/p6.cpp
Modified:
    cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/include/clang/Parse/Action.h
    cfe/trunk/include/clang/Parse/Parser.h
    cfe/trunk/lib/Parse/DeclSpec.cpp
    cfe/trunk/lib/Parse/ParseCXXInlineMethods.cpp
    cfe/trunk/lib/Parse/ParseDecl.cpp
    cfe/trunk/lib/Parse/ParseDeclCXX.cpp
    cfe/trunk/lib/Sema/Sema.h
    cfe/trunk/lib/Sema/SemaDecl.cpp
    cfe/trunk/lib/Sema/SemaDeclCXX.cpp
    cfe/trunk/lib/Sema/SemaLookup.cpp
    cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.unqual/p3.cpp
    cfe/trunk/test/Parser/cxx-friend.cpp

Modified: cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td?rev=78274&r1=78273&r2=78274&view=diff

==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td Wed Aug  5 21:15:43 2009
@@ -35,6 +35,7 @@
 def err_invalid_long_spec : Error<"'long %0' is invalid">;
 def err_invalid_longlong_spec : Error<"'long long %0' is invalid">;
 def err_invalid_complex_spec : Error<"'_Complex %0' is invalid">;
+def err_friend_storage_spec : Error<"'%0' is invalid in friend declarations">;
 
 def ext_ident_list_in_param : Extension<
   "type-less parameter names in function declaration">;
@@ -152,6 +153,8 @@
   "type name does not allow function specifier to be specified">;
 def err_invalid_decl_spec_combination : Error<
   "cannot combine with previous '%0' declaration specifier">;
+def err_friend_invalid_in_context : Error<
+  "'friend' used outside of class">;
 def err_unknown_typename : Error<
   "unknown type name %0">;
 def err_use_of_tag_name_without_tag : Error<

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=78274&r1=78273&r2=78274&view=diff

==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Wed Aug  5 21:15:43 2009
@@ -293,8 +293,20 @@
   "static_assert expression is not an integral constant expression">;
 def err_static_assert_failed : Error<"static_assert failed \"%0\"">;
 
-def err_friend_decl_outside_class : Error<
-  "'friend' used outside of class">;
+def err_friend_decl_defines_class : Error<
+  "cannot define a type in a friend declaration">;
+def err_unexpected_friend : Error<
+  "friends can only be classes or functions">;
+def err_friend_is_member : Error<
+  "friends cannot be members of the declaring class">;
+def err_unelaborated_friend_type : Error<
+  "must specify '%select{class|union}0' in a friend "
+  "%select{class|union}0 declaration">;
+def err_qualified_friend_not_found : Error<
+  "no function named %0 with type %1 was found in the specified scope">;
+def err_introducing_special_friend : Error<
+  "must use a qualified name when declaring a %select{constructor|"
+  "destructor|conversion operator}0 as a friend">;
 
 def err_abstract_type_in_decl : Error<
   "%select{return|parameter|variable|field}1 type %0 is an abstract class">;

Modified: cfe/trunk/include/clang/Parse/Action.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/Action.h?rev=78274&r1=78273&r2=78274&view=diff

==============================================================================
--- cfe/trunk/include/clang/Parse/Action.h (original)
+++ cfe/trunk/include/clang/Parse/Action.h Wed Aug  5 21:15:43 2009
@@ -22,6 +22,7 @@
 #include "clang/Parse/DeclSpec.h"
 #include "clang/Parse/Ownership.h"
 #include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/ADT/PointerUnion.h"
 
 namespace clang {
   // Semantic.
@@ -413,7 +414,8 @@
   enum TagUseKind {
     TUK_Reference,   // Reference to a tag:  'struct foo *X;'
     TUK_Declaration, // Fwd decl of a tag:   'struct foo;'
-    TUK_Definition   // Definition of a tag: 'struct foo { int X; } Y;'
+    TUK_Definition,  // Definition of a tag: 'struct foo { int X; } Y;'
+    TUK_Friend       // Friend declaration:  'friend struct foo;'
   };
 
   /// \brief The parser has encountered a tag (e.g., "class X") that should be
@@ -1109,12 +1111,11 @@
   }
 
   /// ActOnFriendDecl - This action is called when a friend declaration is
-  /// encountered. Returns false on success.
-  virtual bool ActOnFriendDecl(Scope *S, SourceLocation FriendLoc,
-                               DeclPtrTy Dcl) {
-    return false;
+  /// encountered.
+  virtual DeclPtrTy ActOnFriendDecl(Scope *S,
+                        llvm::PointerUnion<const DeclSpec*,Declarator*> D) {
+    return DeclPtrTy();
   }
-  
 
   //===------------------------- C++ Expressions --------------------------===//
 

Modified: cfe/trunk/include/clang/Parse/Parser.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/Parser.h?rev=78274&r1=78273&r2=78274&view=diff

==============================================================================
--- cfe/trunk/include/clang/Parse/Parser.h (original)
+++ cfe/trunk/include/clang/Parse/Parser.h Wed Aug  5 21:15:43 2009
@@ -906,6 +906,14 @@
 
   //===--------------------------------------------------------------------===//
   // C99 6.7: Declarations.
+
+  /// A context for parsing declaration specifiers.  TODO: flesh this
+  /// out, there are other significant restrictions on specifiers than
+  /// would be best implemented in the parser.
+  enum DeclSpecContext {
+    DSC_normal, // normal context
+    DSC_class   // class context, enables 'friend'
+  };
   
   DeclGroupPtrTy ParseDeclaration(unsigned Context, SourceLocation &DeclEnd);
   DeclGroupPtrTy ParseSimpleDeclaration(unsigned Context,
@@ -922,7 +930,8 @@
                         AccessSpecifier AS);
   void ParseDeclarationSpecifiers(DeclSpec &DS, 
                 const ParsedTemplateInfo &TemplateInfo = ParsedTemplateInfo(),
-                                  AccessSpecifier AS = AS_none);
+                                  AccessSpecifier AS = AS_none,
+                                  DeclSpecContext DSC = DSC_normal);
   bool ParseOptionalTypeSpecifier(DeclSpec &DS, bool &isInvalid, 
                                   const char *&PrevSpec,
                                   unsigned &DiagID,

Modified: cfe/trunk/lib/Parse/DeclSpec.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/DeclSpec.cpp?rev=78274&r1=78273&r2=78274&view=diff

==============================================================================
--- cfe/trunk/lib/Parse/DeclSpec.cpp (original)
+++ cfe/trunk/lib/Parse/DeclSpec.cpp Wed Aug  5 21:15:43 2009
@@ -398,6 +398,24 @@
     }
   }
 
+  // C++ [class.friend]p6:
+  //   No storage-class-specifier shall appear in the decl-specifier-seq
+  //   of a friend declaration.
+  if (isFriendSpecified() && getStorageClassSpec()) {
+    DeclSpec::SCS SC = getStorageClassSpec();
+    const char *SpecName = getSpecifierName(SC);
+
+    SourceLocation SCLoc = getStorageClassSpecLoc();
+    SourceLocation SCEndLoc = SCLoc.getFileLocWithOffset(strlen(SpecName));
+
+    Diag(D, SCLoc, SrcMgr, diag::err_friend_storage_spec)
+      << SpecName
+      << CodeModificationHint::CreateRemoval(SourceRange(SCLoc, SCEndLoc));
+
+    ClearStorageClassSpecs();
+  }
+
+
   // Okay, now we can infer the real type.
   
   // TODO: return "auto function" and other bad things based on the real type.

Modified: cfe/trunk/lib/Parse/ParseCXXInlineMethods.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseCXXInlineMethods.cpp?rev=78274&r1=78273&r2=78274&view=diff

==============================================================================
--- cfe/trunk/lib/Parse/ParseCXXInlineMethods.cpp (original)
+++ cfe/trunk/lib/Parse/ParseCXXInlineMethods.cpp Wed Aug  5 21:15:43 2009
@@ -27,7 +27,11 @@
   assert((Tok.is(tok::l_brace) || Tok.is(tok::colon) || Tok.is(tok::kw_try)) &&
          "Current token not a '{', ':' or 'try'!");
 
-  DeclPtrTy FnD = Actions.ActOnCXXMemberDeclarator(CurScope, AS, D, 0, 0);
+  DeclPtrTy FnD;
+  if (D.getDeclSpec().isFriendSpecified())
+    FnD = Actions.ActOnFriendDecl(CurScope, &D);
+  else
+    FnD = Actions.ActOnCXXMemberDeclarator(CurScope, AS, D, 0, 0);
 
   HandleMemberFunctionDefaultArgs(D, FnD);
 

Modified: cfe/trunk/lib/Parse/ParseDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseDecl.cpp?rev=78274&r1=78273&r2=78274&view=diff

==============================================================================
--- cfe/trunk/lib/Parse/ParseDecl.cpp (original)
+++ cfe/trunk/lib/Parse/ParseDecl.cpp Wed Aug  5 21:15:43 2009
@@ -708,7 +708,8 @@
 ///
 void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
                                         const ParsedTemplateInfo &TemplateInfo,
-                                        AccessSpecifier AS) {
+                                        AccessSpecifier AS,
+                                        DeclSpecContext DSContext) {
   DS.SetRangeStart(Tok.getLocation());
   while (1) {
     bool isInvalid = false;
@@ -968,7 +969,13 @@
 
     // friend
     case tok::kw_friend:
-      isInvalid = DS.SetFriendSpec(Loc, PrevSpec, DiagID);
+      if (DSContext == DSC_class)
+        isInvalid = DS.SetFriendSpec(Loc, PrevSpec, DiagID);
+      else {
+        PrevSpec = ""; // not actually used by the diagnostic
+        DiagID = diag::err_friend_invalid_in_context;
+        isInvalid = true;
+      }
       break;
       
     // type-specifier

Modified: cfe/trunk/lib/Parse/ParseDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseDeclCXX.cpp?rev=78274&r1=78273&r2=78274&view=diff

==============================================================================
--- cfe/trunk/lib/Parse/ParseDeclCXX.cpp (original)
+++ cfe/trunk/lib/Parse/ParseDeclCXX.cpp Wed Aug  5 21:15:43 2009
@@ -567,15 +567,16 @@
     }
   }
 
-  // There are three options here.  If we have 'struct foo;', then
-  // this is a forward declaration.  If we have 'struct foo {...' or
+  // There are four options here.  If we have 'struct foo;', then this
+  // is either a forward declaration or a friend declaration, which
+  // have to be treated differently.  If we have 'struct foo {...' or
   // 'struct foo :...' then this is a definition. Otherwise we have
   // something like 'struct foo xyz', a reference.
   Action::TagUseKind TUK;
   if (Tok.is(tok::l_brace) || (getLang().CPlusPlus && Tok.is(tok::colon)))
     TUK = Action::TUK_Definition;
-  else if (Tok.is(tok::semi) && !DS.isFriendSpecified())
-    TUK = Action::TUK_Declaration;
+  else if (Tok.is(tok::semi))
+    TUK = DS.isFriendSpecified() ? Action::TUK_Friend : Action::TUK_Declaration;
   else
     TUK = Action::TUK_Reference;
 
@@ -600,7 +601,7 @@
   // to turn that template-id into a type.
 
   bool Owned = false;
-  if (TemplateId && TUK != Action::TUK_Reference) {
+  if (TemplateId && TUK != Action::TUK_Reference && TUK != Action::TUK_Friend) {
     // Explicit specialization, class template partial specialization,
     // or explicit instantiation.
     ASTTemplateArgsPtr TemplateArgsPtr(Actions, 
@@ -727,10 +728,6 @@
   if (DS.SetTypeSpecType(TagType, StartLoc, PrevSpec, DiagID,
                          TagOrTempResult.get().getAs<void>(), Owned))
     Diag(StartLoc, DiagID) << PrevSpec;
-  
-  if (DS.isFriendSpecified())
-    Actions.ActOnFriendDecl(CurScope, DS.getFriendSpecLoc(), 
-                            TagOrTempResult.get());
 }
 
 /// ParseBaseClause - Parse the base-clause of a C++ class [C++ class.derived]. 
@@ -951,24 +948,17 @@
   // decl-specifier-seq:
   // Parse the common declaration-specifiers piece.
   DeclSpec DS;
-  ParseDeclarationSpecifiers(DS, ParsedTemplateInfo(), AS);
+  ParseDeclarationSpecifiers(DS, ParsedTemplateInfo(), AS, DSC_class);
 
   if (Tok.is(tok::semi)) {
     ConsumeToken();
-    // C++ 9.2p7: The member-declarator-list can be omitted only after a
-    // class-specifier or an enum-specifier or in a friend declaration.
-    // FIXME: Friend declarations.
-    switch (DS.getTypeSpecType()) {
-    case DeclSpec::TST_struct:
-    case DeclSpec::TST_union:
-    case DeclSpec::TST_class:
-    case DeclSpec::TST_enum:
+
+    if (DS.isFriendSpecified())
+      Actions.ActOnFriendDecl(CurScope, &DS);
+    else
       Actions.ParsedFreeStandingDeclSpec(CurScope, DS);
-      return;
-    default:
-      Diag(DSStart, diag::err_no_declarators);
-      return;
-    }
+
+    return;
   }
 
   Declarator DeclaratorInfo(DS, Declarator::MemberContext);
@@ -1066,11 +1056,17 @@
     // NOTE: If Sema is the Action module and declarator is an instance field,
     // this call will *not* return the created decl; It will return null.
     // See Sema::ActOnCXXMemberDeclarator for details.
-    DeclPtrTy ThisDecl = Actions.ActOnCXXMemberDeclarator(CurScope, AS,
-                                                          DeclaratorInfo,
-                                                          BitfieldSize.release(),
-                                                          Init.release(),
-                                                          Deleted);
+
+    DeclPtrTy ThisDecl;
+    if (DS.isFriendSpecified()) {
+      // TODO: handle initializers, bitfields, 'delete'
+      ThisDecl = Actions.ActOnFriendDecl(CurScope, &DeclaratorInfo);
+    } else
+      ThisDecl = Actions.ActOnCXXMemberDeclarator(CurScope, AS,
+                                                  DeclaratorInfo,
+                                                  BitfieldSize.release(),
+                                                  Init.release(),
+                                                  Deleted);
     if (ThisDecl)
       DeclsInGroup.push_back(ThisDecl);
 

Modified: cfe/trunk/lib/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.h?rev=78274&r1=78273&r2=78274&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/Sema.h (original)
+++ cfe/trunk/lib/Sema/Sema.h Wed Aug  5 21:15:43 2009
@@ -1192,6 +1192,9 @@
   LookupResult LookupQualifiedName(DeclContext *LookupCtx, DeclarationName Name,
                                    LookupNameKind NameKind, 
                                    bool RedeclarationOnly = false);
+  Decl *LookupQualifiedNameWithType(DeclContext *LookupCtx,
+                                    DeclarationName Name,
+                                    QualType T);
   LookupResult LookupParsedName(Scope *S, const CXXScopeSpec *SS, 
                                 DeclarationName Name,
                                 LookupNameKind NameKind, 
@@ -2001,8 +2004,8 @@
                                                  ExprArg AssertExpr,
                                                  ExprArg AssertMessageExpr);
   
-  virtual bool ActOnFriendDecl(Scope *S, SourceLocation FriendLoc,
-                               DeclPtrTy Dcl);
+  virtual DeclPtrTy ActOnFriendDecl(Scope *S,
+                    llvm::PointerUnion<const DeclSpec*,Declarator*> D);
 
   QualType CheckConstructorDeclarator(Declarator &D, QualType R,
                                       FunctionDecl::StorageClass& SC);

Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=78274&r1=78273&r2=78274&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Wed Aug  5 21:15:43 2009
@@ -1275,6 +1275,9 @@
     if (!DS.getTypeRep()) // We probably had an error
       return DeclPtrTy();
 
+    // Note that the above type specs guarantee that the
+    // type rep is a Decl, whereas in many of the others
+    // it's a Type.
     Tag = dyn_cast<TagDecl>(static_cast<Decl *>(DS.getTypeRep()));
   }
 
@@ -3973,7 +3976,8 @@
       // If this is a use of a previous tag, or if the tag is already declared
       // in the same scope (so that the definition/declaration completes or
       // rementions the tag), reuse the decl.
-      if (TUK == TUK_Reference || isDeclInScope(PrevDecl, SearchDC, S)) {
+      if (TUK == TUK_Reference || TUK == TUK_Friend ||
+          isDeclInScope(PrevDecl, SearchDC, S)) {
         // Make sure that this wasn't declared as an enum and now used as a
         // struct or something similar.
         if (!isAcceptableTagRedeclaration(PrevTagDecl, Kind, KWLoc, *Name)) {
@@ -4009,6 +4013,11 @@
           if (TUK == TUK_Reference)
             return DeclPtrTy::make(PrevDecl);
 
+          // If this is a friend, make sure we create the new
+          // declaration in the appropriate semantic context.
+          if (TUK == TUK_Friend)
+            SearchDC = PrevDecl->getDeclContext();
+
           // Diagnose attempts to redefine a tag.
           if (TUK == TUK_Definition) {
             if (TagDecl *Def = PrevTagDecl->getDefinition(Context)) {
@@ -4039,7 +4048,8 @@
           }
         }
         // If we get here we have (another) forward declaration or we
-        // have a definition.  Just create a new decl.        
+        // have a definition.  Just create a new decl.
+
       } else {
         // If we get here, this is a definition of a new tag type in a nested
         // scope, e.g. "struct foo; void bar() { struct foo; }", just create a 
@@ -4102,6 +4112,18 @@
            (S->getEntity() && 
             ((DeclContext *)S->getEntity())->isTransparentContext()))
       S = S->getParent();
+
+  } else if (TUK == TUK_Friend && SS.isEmpty() && Name) {
+    // C++ [namespace.memdef]p3:
+    //   If a friend declaration in a non-local class first declares a
+    //   class or function, the friend class or function is a member of
+    //   the innermost enclosing namespace.
+    while (!SearchDC->isNamespace() && !SearchDC->isTranslationUnit())
+      SearchDC = SearchDC->getParent();
+
+    // The entity of a decl scope is a DeclContext; see PushDeclContext.
+    while (S->getEntity() != SearchDC)
+      S = S->getParent();
   }
 
 CreateNewDecl:
@@ -4195,14 +4217,14 @@
   New->setLexicalDeclContext(CurContext);
 
   // Set the access specifier.
-  if (!Invalid)
+  if (!Invalid && TUK != TUK_Friend)
     SetMemberAccessSpecifier(New, PrevDecl, AS);
 
   if (TUK == TUK_Definition)
     New->startDefinition();
   
   // If this has an identifier, add it to the scope stack.
-  if (Name) {
+  if (Name && TUK != TUK_Friend) {
     S = getNonFieldDeclScope(S);
     PushOnScopeChains(New, S);
   } else {

Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=78274&r1=78273&r2=78274&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Wed Aug  5 21:15:43 2009
@@ -550,6 +550,8 @@
 
   bool isFunc = D.isFunctionDeclarator();
 
+  assert(!DS.isFriendSpecified());
+
   // C++ 9.2p6: A member shall not be declared to have automatic storage
   // duration (auto, register) or with the extern storage-class-specifier.
   // C++ 7.1.1p8: The mutable specifier can be applied only to names of class
@@ -3302,13 +3304,208 @@
   return DeclPtrTy::make(Decl);
 }
 
-bool Sema::ActOnFriendDecl(Scope *S, SourceLocation FriendLoc, DeclPtrTy Dcl) {
-  if (!(S->getFlags() & Scope::ClassScope)) {
-    Diag(FriendLoc, diag::err_friend_decl_outside_class);
-    return true;
+Sema::DeclPtrTy Sema::ActOnFriendDecl(Scope *S,
+                       llvm::PointerUnion<const DeclSpec*,Declarator*> DU) {
+  Declarator *D = DU.dyn_cast<Declarator*>();
+  const DeclSpec &DS = (D ? D->getDeclSpec() : *DU.get<const DeclSpec*>());
+
+  assert(DS.isFriendSpecified());
+  assert(DS.getStorageClassSpec() == DeclSpec::SCS_unspecified);
+
+  // If there's no declarator, then this can only be a friend class
+  // declaration (or else it's just invalid).
+  if (!D) {
+
+    // C++ [class.friend]p2:
+    //   An elaborated-type-specifier shall be used in a friend declaration
+    //   for a class.*
+    //   * The class-key of the elaborated-type-specifier is required.
+    CXXRecordDecl *RD = 0;
+
+    switch (DS.getTypeSpecType()) {
+    case DeclSpec::TST_class:
+    case DeclSpec::TST_struct:
+    case DeclSpec::TST_union:
+      RD = dyn_cast_or_null<CXXRecordDecl>(static_cast<Decl*>(DS.getTypeRep()));
+      if (!RD) return DeclPtrTy();
+      break;
+
+    case DeclSpec::TST_typename:
+      if (const RecordType *RT = 
+          ((const Type*) DS.getTypeRep())->getAs<RecordType>())
+        RD = dyn_cast<CXXRecordDecl>(RT->getDecl());
+      // fallthrough
+    default:
+      if (RD) {
+        Diag(DS.getFriendSpecLoc(), diag::err_unelaborated_friend_type)
+          << (RD->isUnion())
+          << CodeModificationHint::CreateInsertion(DS.getTypeSpecTypeLoc(),
+                                      RD->isUnion() ? " union" : " class");
+        return DeclPtrTy::make(RD);
+      }
+
+      Diag(DS.getFriendSpecLoc(), diag::err_unexpected_friend)
+        << DS.getSourceRange();
+      return DeclPtrTy();
+    }
+
+    // The record declaration we get from friend declarations is not
+    // canonicalized; see ActOnTag.
+    assert(RD);
+
+    // C++ [class.friend]p2: A class shall not be defined inside
+    //   a friend declaration.
+    if (RD->isDefinition())
+      Diag(DS.getFriendSpecLoc(), diag::err_friend_decl_defines_class)
+        << RD->getSourceRange();
+
+    // C++ [class.friend]p1: A friend of a class is a function or
+    //   class that is not a member of the class . . .
+    // Definitions currently get treated in a way that causes this
+    // error, so only report it if we didn't see a definition.
+    else if (RD->getDeclContext() == CurContext)
+      Diag(DS.getFriendSpecLoc(), diag::err_friend_is_member);
+
+    return DeclPtrTy::make(RD);
+  }
+
+  // We have a declarator.
+  assert(D);
+
+  SourceLocation Loc = D->getIdentifierLoc();
+  QualType T = GetTypeForDeclarator(*D, S);
+
+  // C++ [class.friend]p1
+  //   A friend of a class is a function or class....
+  // Note that this sees through typedefs, which is intended.
+  if (!T->isFunctionType()) {
+    Diag(Loc, diag::err_unexpected_friend);
+
+    // It might be worthwhile to try to recover by creating an
+    // appropriate declaration.
+    return DeclPtrTy();
   }
-  
-  return false;
+
+  // C++ [namespace.memdef]p3
+  //  - If a friend declaration in a non-local class first declares a
+  //    class or function, the friend class or function is a member
+  //    of the innermost enclosing namespace.
+  //  - The name of the friend is not found by simple name lookup
+  //    until a matching declaration is provided in that namespace
+  //    scope (either before or after the class declaration granting
+  //    friendship).
+  //  - If a friend function is called, its name may be found by the
+  //    name lookup that considers functions from namespaces and
+  //    classes associated with the types of the function arguments.
+  //  - When looking for a prior declaration of a class or a function
+  //    declared as a friend, scopes outside the innermost enclosing
+  //    namespace scope are not considered.
+
+  CXXScopeSpec &ScopeQual = D->getCXXScopeSpec();
+  DeclarationName Name = GetNameForDeclarator(*D);
+  assert(Name);
+
+  // The existing declaration we found.
+  FunctionDecl *FD = NULL;
+
+  // The context we found the declaration in, or in which we should
+  // create the declaration.
+  DeclContext *DC;
+
+  // FIXME: handle local classes
+
+  // Recover from invalid scope qualifiers as if they just weren't there.
+  if (!ScopeQual.isInvalid() && ScopeQual.isSet()) {
+    DC = computeDeclContext(ScopeQual);
+
+    // FIXME: handle dependent contexts
+    if (!DC) return DeclPtrTy();
+
+    Decl *Dec = LookupQualifiedNameWithType(DC, Name, T);
+
+    // If searching in that context implicitly found a declaration in
+    // a different context, treat it like it wasn't found at all.
+    // TODO: better diagnostics for this case.  Suggesting the right
+    // qualified scope would be nice...
+    if (!Dec || Dec->getDeclContext() != DC) {
+      D->setInvalidType();
+      Diag(Loc, diag::err_qualified_friend_not_found) << Name << T;
+      return DeclPtrTy();
+    }
+
+    // C++ [class.friend]p1: A friend of a class is a function or
+    //   class that is not a member of the class . . .
+    if (DC == CurContext)
+      Diag(DS.getFriendSpecLoc(), diag::err_friend_is_member);
+
+    FD = cast<FunctionDecl>(Dec);
+
+  // Otherwise walk out to the nearest namespace scope looking for matches.
+  } else {
+    // TODO: handle local class contexts.
+
+    DC = CurContext;
+    while (true) {
+      // Skip class contexts.  If someone can cite chapter and verse
+      // for this behavior, that would be nice --- it's what GCC and
+      // EDG do, and it seems like a reasonable intent, but the spec
+      // really only says that checks for unqualified existing
+      // declarations should stop at the nearest enclosing namespace,
+      // not that they should only consider the nearest enclosing
+      // namespace.
+      while (DC->isRecord()) DC = DC->getParent();
+
+      Decl *Dec = LookupQualifiedNameWithType(DC, Name, T);
+
+      // TODO: decide what we think about using declarations.
+      if (Dec) {
+        FD = cast<FunctionDecl>(Dec);
+        break;
+      }
+      if (DC->isFileContext()) break;
+      DC = DC->getParent();
+    }
+
+    // C++ [class.friend]p1: A friend of a class is a function or
+    //   class that is not a member of the class . . .
+    if (FD && DC == CurContext)
+      Diag(DS.getFriendSpecLoc(), diag::err_friend_is_member);
+  }
+
+  // If we didn't find something matching the type exactly, create
+  // a declaration.  This declaration should only be findable via
+  // argument-dependent lookup.
+  if (!FD) {
+    assert(DC->isFileContext());
+
+    // This implies that it has to be an operator or function.
+    if (D->getKind() == Declarator::DK_Constructor ||
+        D->getKind() == Declarator::DK_Destructor ||
+        D->getKind() == Declarator::DK_Conversion) {
+      Diag(Loc, diag::err_introducing_special_friend) <<
+        (D->getKind() == Declarator::DK_Constructor ? 0 :
+         D->getKind() == Declarator::DK_Destructor ? 1 : 2);
+      return DeclPtrTy();
+    }
+
+    bool Redeclaration = false;
+    NamedDecl *ND = ActOnFunctionDeclarator(S, *D, DC, T,
+                                            /* PrevDecl = */ NULL,
+                                            MultiTemplateParamsArg(*this),
+                                            /* isFunctionDef */ false,
+                                            Redeclaration);
+
+    FD = cast_or_null<FunctionDecl>(ND);
+
+    // Note that we're creating a declaration but *not* pushing
+    // it onto the scope chains.
+
+    // TODO: make accessible via argument-dependent lookup.
+  }
+
+  // TODO: actually register the function as a friend.
+
+  return DeclPtrTy::make(FD);
 }
 
 void Sema::SetDeclDeleted(DeclPtrTy dcl, SourceLocation DelLoc) {

Modified: cfe/trunk/lib/Sema/SemaLookup.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaLookup.cpp?rev=78274&r1=78273&r2=78274&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaLookup.cpp (original)
+++ cfe/trunk/lib/Sema/SemaLookup.cpp Wed Aug  5 21:15:43 2009
@@ -1677,6 +1677,26 @@
   return cast_or_null<ObjCCategoryImplDecl>(D);
 }
 
+// Attempts to find a declaration in the given declaration context
+// with exactly the given type.  Returns null if no such declaration
+// was found.
+Decl *Sema::LookupQualifiedNameWithType(DeclContext *DC,
+                                        DeclarationName Name,
+                                        QualType T) {
+  LookupResult result =
+    LookupQualifiedName(DC, Name, LookupOrdinaryName, true);
+
+  CanQualType CQT = Context.getCanonicalType(T);
+
+  for (LookupResult::iterator ir = result.begin(), ie = result.end();
+       ir != ie; ++ir)
+    if (FunctionDecl *CurFD = dyn_cast<FunctionDecl>(*ir))
+      if (Context.getCanonicalType(CurFD->getType()) == CQT)
+        return CurFD;
+
+  return NULL;
+}
+
 void Sema::LookupOverloadedOperatorName(OverloadedOperatorKind Op, Scope *S,
                                         QualType T1, QualType T2, 
                                         FunctionSet &Functions) {

Modified: cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.unqual/p3.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.unqual/p3.cpp?rev=78274&r1=78273&r2=78274&view=diff

==============================================================================
--- cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.unqual/p3.cpp (original)
+++ cfe/trunk/test/CXX/basic/basic.lookup/basic.lookup.unqual/p3.cpp Wed Aug  5 21:15:43 2009
@@ -1,9 +1,7 @@
 // RUN: clang-cc -fsyntax-only -verify %s
-// XFAIL
 
-// FIXME: This part is here to demonstrate the failure in looking up 'f', it can
-// be removed once the whole test passes.
 typedef int f; 
+
 namespace N0 {
   struct A { 
     friend void f(); 

Added: cfe/trunk/test/CXX/class/class.friend/p1.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/class/class.friend/p1.cpp?rev=78274&view=auto

==============================================================================
--- cfe/trunk/test/CXX/class/class.friend/p1.cpp (added)
+++ cfe/trunk/test/CXX/class/class.friend/p1.cpp Wed Aug  5 21:15:43 2009
@@ -0,0 +1,59 @@
+// RUN: clang-cc -fsyntax-only -verify %s
+
+struct Outer {
+  struct Inner {
+    int intfield;
+  };
+};
+
+struct Base {
+  void base_member();
+  
+  typedef int Int;
+  Int typedeffed_member();
+};
+
+struct Derived : public Base {
+};
+
+int myglobal;
+
+class A {
+  class AInner {
+  };
+
+  friend class PreDeclared;
+  friend class Outer::Inner;
+  friend int Outer::Inner::intfield; // expected-error {{ friends can only be classes or functions }}
+  friend int Outer::Inner::missing_field; //expected-error {{ friends can only be classes or functions }}
+  friend int myoperation(float); // okay
+  friend int myglobal;   // expected-error {{ friends can only be classes or functions }}
+
+  void a_member();
+  friend void A::a_member(); // expected-error {{ friends cannot be members of the declaring class }}
+  friend void a_member(); // okay (because we ignore class scopes when looking up friends)
+  friend class A::AInner; // expected-error {{ friends cannot be members of the declaring class }}
+  friend class AInner; // expected-error {{ friends cannot be members of the declaring class }}
+
+  friend void Derived::missing_member(); // expected-error {{ no function named 'missing_member' with type 'void ()' was found in the specified scope }}
+
+  friend void Derived::base_member(); // expected-error {{ no function named 'base_member' with type 'void ()' was found in the specified scope }}
+
+  friend int Base::typedeffed_member(); // okay: should look through typedef
+
+  // These test that the friend is properly not being treated as a
+  // member function.
+  friend A operator|(const A& l, const A& r); // okay
+  friend A operator|(const A& r); // expected-error {{ overloaded 'operator|' must be a binary operator (has 1 parameter) }}
+
+  friend operator bool() const; // expected-error {{ must use a qualified name when declaring a conversion operator as a friend }}
+
+  typedef void ftypedef();
+  friend ftypedef typedeffed_function; // okay (because it's not declared as a member)
+};
+
+class PreDeclared;
+
+int myoperation(float f) {
+  return (int) f;
+}

Added: cfe/trunk/test/CXX/class/class.friend/p2.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/class/class.friend/p2.cpp?rev=78274&view=auto

==============================================================================
--- cfe/trunk/test/CXX/class/class.friend/p2.cpp (added)
+++ cfe/trunk/test/CXX/class/class.friend/p2.cpp Wed Aug  5 21:15:43 2009
@@ -0,0 +1,8 @@
+// RUN: clang-cc -fsyntax-only -verify %s
+
+class A {
+  friend class B {}; // expected-error {{cannot define a type in a friend declaration}}
+  friend int; // expected-error {{friends can only be classes or functions}}
+  friend B; // expected-error {{must specify 'class' in a friend class declaration}}
+  friend class C; // okay
+};

Added: cfe/trunk/test/CXX/class/class.friend/p6.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/class/class.friend/p6.cpp?rev=78274&view=auto

==============================================================================
--- cfe/trunk/test/CXX/class/class.friend/p6.cpp (added)
+++ cfe/trunk/test/CXX/class/class.friend/p6.cpp Wed Aug  5 21:15:43 2009
@@ -0,0 +1,10 @@
+// RUN: clang-cc -fsyntax-only -verify %s
+
+class A {
+  friend static class B; // expected-error {{'static' is invalid in friend declarations}}
+  friend extern class C; // expected-error {{'extern' is invalid in friend declarations}}
+  friend auto class D; // expected-error {{'auto' is invalid in friend declarations}}
+  friend register class E; // expected-error {{'register' is invalid in friend declarations}}
+  friend mutable class F; // expected-error {{'mutable' is invalid in friend declarations}}
+  friend typedef class G; // expected-error {{'typedef' is invalid in friend declarations}}
+};

Modified: cfe/trunk/test/Parser/cxx-friend.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Parser/cxx-friend.cpp?rev=78274&r1=78273&r2=78274&view=diff

==============================================================================
--- cfe/trunk/test/Parser/cxx-friend.cpp (original)
+++ cfe/trunk/test/Parser/cxx-friend.cpp Wed Aug  5 21:15:43 2009
@@ -1,4 +1,4 @@
-// RUN: clang-cc -fsyntax-only %s
+// RUN: clang-cc -fsyntax-only -verify %s
 
 class C {
   friend class D;
@@ -9,9 +9,24 @@
 	void f();
 };
 
+friend int x; // expected-error {{'friend' used outside of class}}
+
+friend class D {}; // expected-error {{'friend' used outside of class}}
+
+union U {
+  int u1;
+};
+
 class B {
   // 'A' here should refer to the declaration above.  
   friend class A;
 
- void f(A *a) { a->f(); }
+  friend C; // expected-error {{must specify 'class' in a friend class declaration}}
+  friend U; // expected-error {{must specify 'union' in a friend union declaration}}
+  friend int; // expected-error {{friends can only be classes or functions}}
+
+  friend void myfunc();
+
+  void f(A *a) { a->f(); }
 };
+





More information about the cfe-commits mailing list