Index: test/Parser/cxx-friend.cpp =================================================================== --- test/Parser/cxx-friend.cpp (revision 78102) +++ test/Parser/cxx-friend.cpp (working copy) @@ -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 @@ public: 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(); } }; + Index: test/CXX/basic/basic.lookup/basic.lookup.unqual/p3.cpp =================================================================== --- test/CXX/basic/basic.lookup/basic.lookup.unqual/p3.cpp (revision 78102) +++ test/CXX/basic/basic.lookup/basic.lookup.unqual/p3.cpp (working copy) @@ -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(); Index: test/CXX/class/class.friend/p1.cpp =================================================================== --- test/CXX/class/class.friend/p1.cpp (revision 0) +++ test/CXX/class/class.friend/p1.cpp (revision 0) @@ -0,0 +1,37 @@ +// RUN: clang-cc -fsyntax-only -verify %s + +struct Outer { + struct Inner { + int intfield; + }; +}; + +int myglobal; + +class A { + class AInner { + }; + + friend class PreDeclared; + friend class Outer::Inner; // TODO: this should fail, it's inaccessible + 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(); // expected-error {{ friends cannot be members of the declaring class }} + friend class AInner; // expected-error {{ friends cannot be members of the declaring class }} + + // 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) }} +}; + +class PreDeclared; + +int myoperation(float f) { + return (int) f; +} Index: test/CXX/class/class.friend/p2.cpp =================================================================== --- test/CXX/class/class.friend/p2.cpp (revision 0) +++ test/CXX/class/class.friend/p2.cpp (revision 0) @@ -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 +}; Index: test/CXX/class/class.friend/p6.cpp =================================================================== --- test/CXX/class/class.friend/p6.cpp (revision 0) +++ test/CXX/class/class.friend/p6.cpp (revision 0) @@ -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}} +}; Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td (revision 78102) +++ include/clang/Basic/DiagnosticSemaKinds.td (working copy) @@ -293,8 +293,20 @@ def err_static_assert_expression_is_not_ "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< + "cannot declare a %select{constructor|destructor|conversion operator}0" + " at namespace scope">; def err_abstract_type_in_decl : Error< "%select{return|parameter|variable|field}1 type %0 is an abstract class">; Index: include/clang/Basic/DiagnosticParseKinds.td =================================================================== --- include/clang/Basic/DiagnosticParseKinds.td (revision 78102) +++ include/clang/Basic/DiagnosticParseKinds.td (working copy) @@ -35,6 +35,7 @@ def err_invalid_short_spec : Error<"'sho 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 @@ def err_typename_invalid_functionspec : "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< Index: include/clang/Parse/Parser.h =================================================================== --- include/clang/Parse/Parser.h (revision 78102) +++ include/clang/Parse/Parser.h (working copy) @@ -906,6 +906,14 @@ private: //===--------------------------------------------------------------------===// // 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 @@ private: 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, Index: include/clang/Parse/Action.h =================================================================== --- include/clang/Parse/Action.h (revision 78102) +++ include/clang/Parse/Action.h (working copy) @@ -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 @@ public: 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 @@ public: } /// 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 D) { + return DeclPtrTy(); } - //===------------------------- C++ Expressions --------------------------===// Index: lib/Sema/SemaDeclCXX.cpp =================================================================== --- lib/Sema/SemaDeclCXX.cpp (revision 78102) +++ lib/Sema/SemaDeclCXX.cpp (working copy) @@ -550,6 +550,8 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, 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 @@ -3268,13 +3270,183 @@ Sema::DeclPtrTy Sema::ActOnStaticAssertD 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 DU) { + Declarator *D = DU.dyn_cast(); + const DeclSpec &DS = (D ? D->getDeclSpec() : *DU.get()); + + 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(static_cast(DS.getTypeRep())); + if (!RD) return DeclPtrTy(); + break; + + case DeclSpec::TST_typename: + if (const RecordType *RT = + ((const Type*) DS.getTypeRep())->getAs()) + RD = dyn_cast(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.... + 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); + Decl *Dec = LookupQualifiedNameWithType(DC, Name, T); + + if (!Dec) { + 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(Dec); + + // Otherwise walk out to the nearest namespace scope looking for matches. + } else { + DC = CurContext; + while (true) { + Decl *Dec = LookupQualifiedNameWithType(DC, Name, T); + if (Dec) { + FD = cast(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(ND); + + // 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) { Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp (revision 78102) +++ lib/Sema/SemaDecl.cpp (working copy) @@ -1276,6 +1276,9 @@ Sema::DeclPtrTy Sema::ParsedFreeStanding 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(static_cast(DS.getTypeRep())); } @@ -3974,7 +3977,8 @@ Sema::DeclPtrTy Sema::ActOnTag(Scope *S, // 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)) { @@ -4010,6 +4014,11 @@ Sema::DeclPtrTy Sema::ActOnTag(Scope *S, 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)) { @@ -4040,7 +4049,8 @@ Sema::DeclPtrTy Sema::ActOnTag(Scope *S, } } // 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 @@ -4103,6 +4113,18 @@ Sema::DeclPtrTy Sema::ActOnTag(Scope *S, (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: @@ -4196,14 +4218,14 @@ CreateNewDecl: 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 { Index: lib/Sema/Sema.h =================================================================== --- lib/Sema/Sema.h (revision 78102) +++ lib/Sema/Sema.h (working copy) @@ -1192,6 +1192,9 @@ public: 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, @@ -1993,8 +1996,8 @@ public: ExprArg AssertExpr, ExprArg AssertMessageExpr); - virtual bool ActOnFriendDecl(Scope *S, SourceLocation FriendLoc, - DeclPtrTy Dcl); + virtual DeclPtrTy ActOnFriendDecl(Scope *S, + llvm::PointerUnion D); QualType CheckConstructorDeclarator(Declarator &D, QualType R, FunctionDecl::StorageClass& SC); Index: lib/Sema/SemaLookup.cpp =================================================================== --- lib/Sema/SemaLookup.cpp (revision 78102) +++ lib/Sema/SemaLookup.cpp (working copy) @@ -1677,6 +1677,24 @@ ObjCCategoryImplDecl *Sema::LookupObjCCa return cast_or_null(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); + + for (LookupResult::iterator ir = result.begin(), ie = result.end(); + ir != ie; ++ir) + if (FunctionDecl *CurFD = dyn_cast(*ir)) + if (CurFD->getType() == T) + return CurFD; + + return NULL; +} + void Sema::LookupOverloadedOperatorName(OverloadedOperatorKind Op, Scope *S, QualType T1, QualType T2, FunctionSet &Functions) { Index: lib/Parse/ParseDecl.cpp =================================================================== --- lib/Parse/ParseDecl.cpp (revision 78102) +++ lib/Parse/ParseDecl.cpp (working copy) @@ -708,7 +708,8 @@ bool Parser::ParseImplicitInt(DeclSpec & /// 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 @@ void Parser::ParseDeclarationSpecifiers( // 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 Index: lib/Parse/ParseCXXInlineMethods.cpp =================================================================== --- lib/Parse/ParseCXXInlineMethods.cpp (revision 78102) +++ lib/Parse/ParseCXXInlineMethods.cpp (working copy) @@ -27,7 +27,11 @@ Parser::ParseCXXInlineMethodDef(AccessSp 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); Index: lib/Parse/DeclSpec.cpp =================================================================== --- lib/Parse/DeclSpec.cpp (revision 78102) +++ lib/Parse/DeclSpec.cpp (working copy) @@ -398,6 +398,24 @@ void DeclSpec::Finish(Diagnostic &D, Pre } } + // 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. Index: lib/Parse/ParseDeclCXX.cpp =================================================================== --- lib/Parse/ParseDeclCXX.cpp (revision 78102) +++ lib/Parse/ParseDeclCXX.cpp (working copy) @@ -567,15 +567,16 @@ void Parser::ParseClassSpecifier(tok::To } } - // 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 @@ void Parser::ParseClassSpecifier(tok::To // 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 @@ void Parser::ParseClassSpecifier(tok::To if (DS.SetTypeSpecType(TagType, StartLoc, PrevSpec, DiagID, TagOrTempResult.get().getAs(), 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 @@ void Parser::ParseCXXClassMemberDeclarat // 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 @@ void Parser::ParseCXXClassMemberDeclarat // 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);