First pass at friend semantics.



git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@78274 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Parse/DeclSpec.cpp b/lib/Parse/DeclSpec.cpp
index 8007866..674164a 100644
--- a/lib/Parse/DeclSpec.cpp
+++ b/lib/Parse/DeclSpec.cpp
@@ -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.
diff --git a/lib/Parse/ParseCXXInlineMethods.cpp b/lib/Parse/ParseCXXInlineMethods.cpp
index 8afffe3..52a812d 100644
--- a/lib/Parse/ParseCXXInlineMethods.cpp
+++ b/lib/Parse/ParseCXXInlineMethods.cpp
@@ -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);
 
diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp
index 42ef7e6..a80f57c 100644
--- a/lib/Parse/ParseDecl.cpp
+++ b/lib/Parse/ParseDecl.cpp
@@ -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
diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp
index c0df8a5..5084d8a 100644
--- a/lib/Parse/ParseDeclCXX.cpp
+++ b/lib/Parse/ParseDeclCXX.cpp
@@ -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);
 
diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h
index ca7e02b..0a4086f 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -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);
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index 44ee2eb..90ba65a 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -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 {
diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp
index 90d02ce..240fcd6 100644
--- a/lib/Sema/SemaDeclCXX.cpp
+++ b/lib/Sema/SemaDeclCXX.cpp
@@ -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);
   }
-  
-  return false;
+
+  // 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();
+  }
+
+  // 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) {
diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp
index 788111c..43494cf 100644
--- a/lib/Sema/SemaLookup.cpp
+++ b/lib/Sema/SemaLookup.cpp
@@ -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) {