diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Parse/DeclSpec.cpp | 18 | ||||
-rw-r--r-- | lib/Parse/ParseCXXInlineMethods.cpp | 6 | ||||
-rw-r--r-- | lib/Parse/ParseDecl.cpp | 11 | ||||
-rw-r--r-- | lib/Parse/ParseDeclCXX.cpp | 52 | ||||
-rw-r--r-- | lib/Sema/Sema.h | 7 | ||||
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 30 | ||||
-rw-r--r-- | lib/Sema/SemaDeclCXX.cpp | 209 | ||||
-rw-r--r-- | lib/Sema/SemaLookup.cpp | 20 |
8 files changed, 310 insertions, 43 deletions
diff --git a/lib/Parse/DeclSpec.cpp b/lib/Parse/DeclSpec.cpp index 80078660b6..674164a260 100644 --- a/lib/Parse/DeclSpec.cpp +++ b/lib/Parse/DeclSpec.cpp @@ -398,6 +398,24 @@ void DeclSpec::Finish(Diagnostic &D, Preprocessor &PP) { } } + // 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 8afffe3815..52a812d4ba 100644 --- a/lib/Parse/ParseCXXInlineMethods.cpp +++ b/lib/Parse/ParseCXXInlineMethods.cpp @@ -27,7 +27,11 @@ Parser::ParseCXXInlineMethodDef(AccessSpecifier AS, Declarator &D) { 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 42ef7e6c29..a80f57c8ca 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -708,7 +708,8 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS, /// 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(DeclSpec &DS, // 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 c0df8a59f7..5084d8a831 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -567,15 +567,16 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, } } - // 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::TokenKind TagTokKind, // 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::TokenKind TagTokKind, 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 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS) { // 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::ParseCXXClassMemberDeclaration(AccessSpecifier AS) { // 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 ca7e02bcfd..0a4086fe3e 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -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, @@ -2001,8 +2004,8 @@ public: 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 44ee2ebbf0..90ba65a7ea 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -1275,6 +1275,9 @@ Sema::DeclPtrTy Sema::ParsedFreeStandingDeclSpec(Scope *S, DeclSpec &DS) { 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 @@ Sema::DeclPtrTy Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, // 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 @@ Sema::DeclPtrTy Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, 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 @@ Sema::DeclPtrTy Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, } } // 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 @@ Sema::DeclPtrTy Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, (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 @@ 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 { diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 90d02ce3b5..240fcd66d9 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -550,6 +550,8 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D, 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 @@ Sema::DeclPtrTy Sema::ActOnStaticAssertDeclaration(SourceLocation AssertLoc, 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 788111c5dd..43494cf4aa 100644 --- a/lib/Sema/SemaLookup.cpp +++ b/lib/Sema/SemaLookup.cpp @@ -1677,6 +1677,26 @@ ObjCCategoryImplDecl *Sema::LookupObjCCategoryImpl(IdentifierInfo *II) { 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) { |