diff options
author | John McCall <rjmccall@apple.com> | 2009-08-28 07:59:38 +0000 |
---|---|---|
committer | John McCall <rjmccall@apple.com> | 2009-08-28 07:59:38 +0000 |
commit | 02cace78cf48cc26686bd5b07c78606abca13bcd (patch) | |
tree | 6a631652ec93156bd9ed8b2eafd457d8948a607a /lib/Sema/SemaDeclCXX.cpp | |
parent | 550b14b410eaed037c9b791806194e6cea1ebe90 (diff) |
Omnibus friend decl refactoring. Instead of cloning AST classes for friend
declarations of same, introduce a single AST class and add appropriate bits
(encoded in the namespace) for whether a decl is "real" or not. Much hackery
about previously-declared / not-previously-declared, but it's essentially
mandated by the standard that friends alter lookup, and this is at least
fairly non-intrusive.
Refactor the Sema methods specific to friends for cleaner flow and less nesting.
Incidentally solve a few bugs, but I remain confident that we can put them back.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@80353 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Sema/SemaDeclCXX.cpp')
-rw-r--r-- | lib/Sema/SemaDeclCXX.cpp | 230 |
1 files changed, 119 insertions, 111 deletions
diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 602caf2825..4874c92dec 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -3567,116 +3567,124 @@ Sema::DeclPtrTy Sema::ActOnStaticAssertDeclaration(SourceLocation AssertLoc, Sema::DeclPtrTy Sema::ActOnFriendDecl(Scope *S, llvm::PointerUnion<const DeclSpec*,Declarator*> DU, bool IsDefinition) { - Declarator *D = DU.dyn_cast<Declarator*>(); - const DeclSpec &DS = (D ? D->getDeclSpec() : *DU.get<const DeclSpec*>()); + if (DU.is<Declarator*>()) + return ActOnFriendFunctionDecl(S, *DU.get<Declarator*>(), IsDefinition); + else + return ActOnFriendTypeDecl(S, *DU.get<const DeclSpec*>(), IsDefinition); +} + +Sema::DeclPtrTy Sema::ActOnFriendTypeDecl(Scope *S, + const DeclSpec &DS, + bool IsDefinition) { + SourceLocation Loc = DS.getSourceRange().getBegin(); 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 syntactically invalid). - if (!D) { - SourceLocation Loc = DS.getSourceRange().getBegin(); - - QualType T; - DeclContext *DC; - - // In C++0x, we just accept any old type. - if (getLangOptions().CPlusPlus0x) { - bool invalid = false; - QualType T = ConvertDeclSpecToType(DS, Loc, invalid); - if (invalid) - return DeclPtrTy(); - - // The semantic context in which to create the decl. If it's not - // a record decl (or we don't yet know if it is), create it in the - // current context. - DC = CurContext; - if (const RecordType *RT = T->getAs<RecordType>()) - DC = RT->getDecl()->getDeclContext(); - - // The C++98 rules are somewhat more complex. - } else { - // 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; + // Check to see if the decl spec was syntactically like "struct foo". + RecordDecl *RD = NULL; + + switch (DS.getTypeSpecType()) { + case DeclSpec::TST_class: + case DeclSpec::TST_struct: + case DeclSpec::TST_union: + RD = dyn_cast_or_null<CXXRecordDecl>((Decl*) DS.getTypeRep()); + if (!RD) return DeclPtrTy(); + + // The parser doesn't quite handle + // friend class A {} + // as we'd like, because it might have been the (valid) prefix of + // friend class A {} foo(); + // So even in C++0x mode we don't want to + IsDefinition |= RD->isDefinition(); + break; + + default: break; + } + + FriendDecl::FriendUnion FU = RD; + + // 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. + // So if we didn't get a record decl above, we're invalid in C++98 mode. + if (!RD) { + bool invalid = false; + QualType T = ConvertDeclSpecToType(DS, Loc, invalid); + if (invalid) return DeclPtrTy(); - switch (DS.getTypeSpecType()) { - case DeclSpec::TST_class: - case DeclSpec::TST_struct: - case DeclSpec::TST_union: - RD = dyn_cast_or_null<CXXRecordDecl>((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(); + if (const RecordType *RT = T->getAs<RecordType>()) { + FU = RD = cast<CXXRecordDecl>(RT->getDecl()); + + // Untagged typenames are invalid prior to C++0x, but we can + // suggest an easy fix which should work. + if (!getLangOptions().CPlusPlus0x) { + Diag(DS.getFriendSpecLoc(), diag::err_unelaborated_friend_type) + << (RD->isUnion()) + << CodeModificationHint::CreateInsertion(DS.getTypeSpecTypeLoc(), + RD->isUnion() ? " union" : " class"); return DeclPtrTy(); } - - // The record declaration we get from friend declarations is not - // canonicalized; see ActOnTag. - - // 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++98 [class.friend]p1: A friend of a class is a function - // or class that is not a member of the class . . . - // But that's a silly restriction which nobody implements for - // inner classes, and C++0x removes it anyway, so we only report - // this (as a warning) if we're being pedantic. - // - // Also, 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 && - !getLangOptions().CPlusPlus0x) - Diag(DS.getFriendSpecLoc(), diag::ext_friend_inner_class); - - T = QualType(RD->getTypeForDecl(), 0); - DC = RD->getDeclContext(); + }else if (!getLangOptions().CPlusPlus0x) { + Diag(DS.getFriendSpecLoc(), diag::err_unexpected_friend) + << DS.getSourceRange(); + return DeclPtrTy(); + }else { + FU = T.getTypePtr(); } + } - FriendClassDecl *FCD = FriendClassDecl::Create(Context, DC, Loc, T, - DS.getFriendSpecLoc()); - FCD->setLexicalDeclContext(CurContext); + assert(FU && "should have a friend decl/type by here!"); - if (CurContext->isDependentContext()) - CurContext->addHiddenDecl(FCD); - else - CurContext->addDecl(FCD); + // C++ [class.friend]p2: A class shall not be defined inside + // a friend declaration. + if (IsDefinition) { + Diag(DS.getFriendSpecLoc(), diag::err_friend_decl_defines_class) + << DS.getSourceRange(); + return DeclPtrTy(); + } - return DeclPtrTy::make(FCD); + // C++98 [class.friend]p1: A friend of a class is a function + // or class that is not a member of the class . . . + // But that's a silly restriction which nobody implements for + // inner classes, and C++0x removes it anyway, so we only report + // this (as a warning) if we're being pedantic. + if (!getLangOptions().CPlusPlus0x) { + assert(RD && "must have a record decl in C++98 mode"); + if (RD->getDeclContext() == CurContext) + Diag(DS.getFriendSpecLoc(), diag::ext_friend_inner_class); } - // We have a declarator. - assert(D); + FriendDecl *FD = FriendDecl::Create(Context, CurContext, Loc, FU, + DS.getFriendSpecLoc()); + CurContext->addDecl(FD); - SourceLocation Loc = D->getIdentifierLoc(); + return DeclPtrTy::make(FD); +} + +Sema::DeclPtrTy Sema::ActOnFriendFunctionDecl(Scope *S, + Declarator &D, + bool IsDefinition) { + const DeclSpec &DS = D.getDeclSpec(); + + assert(DS.isFriendSpecified()); + assert(DS.getStorageClassSpec() == DeclSpec::SCS_unspecified); + + SourceLocation Loc = D.getIdentifierLoc(); DeclaratorInfo *DInfo = 0; - QualType T = GetTypeForDeclarator(*D, S, &DInfo); + QualType T = GetTypeForDeclarator(D, S, &DInfo); // C++ [class.friend]p1 // A friend of a class is a function or class.... // Note that this sees through typedefs, which is intended. + // It *doesn't* see through dependent types, which is correct + // according to [temp.arg.type]p3: + // If a declaration acquires a function type through a + // type dependent on a template-parameter and this causes + // a declaration that does not use the syntactic form of a + // function declarator to have a function type, the program + // is ill-formed. if (!T->isFunctionType()) { Diag(Loc, diag::err_unexpected_friend); @@ -3700,8 +3708,8 @@ Sema::DeclPtrTy Sema::ActOnFriendDecl(Scope *S, // declared as a friend, scopes outside the innermost enclosing // namespace scope are not considered. - CXXScopeSpec &ScopeQual = D->getCXXScopeSpec(); - DeclarationName Name = GetNameForDeclarator(*D); + CXXScopeSpec &ScopeQual = D.getCXXScopeSpec(); + DeclarationName Name = GetNameForDeclarator(D); assert(Name); // The existing declaration we found. @@ -3727,7 +3735,7 @@ Sema::DeclPtrTy Sema::ActOnFriendDecl(Scope *S, // TODO: better diagnostics for this case. Suggesting the right // qualified scope would be nice... if (!Dec || Dec->getDeclContext() != DC) { - D->setInvalidType(); + D.setInvalidType(); Diag(Loc, diag::err_qualified_friend_not_found) << Name << T; return DeclPtrTy(); } @@ -3789,36 +3797,36 @@ Sema::DeclPtrTy Sema::ActOnFriendDecl(Scope *S, 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) { + 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); + (D.getKind() == Declarator::DK_Constructor ? 0 : + D.getKind() == Declarator::DK_Destructor ? 1 : 2); return DeclPtrTy(); } } - NamedDecl *ND = ActOnFunctionDeclarator(S, *D, DC, T, DInfo, + NamedDecl *ND = ActOnFunctionDeclarator(S, D, DC, T, DInfo, /* PrevDecl = */ FD, MultiTemplateParamsArg(*this), IsDefinition, Redeclaration); - FD = cast_or_null<FriendFunctionDecl>(ND); + if (!ND) return DeclPtrTy(); + FD = cast<FunctionDecl>(ND); assert(FD->getDeclContext() == DC); assert(FD->getLexicalDeclContext() == CurContext); - // If this is a dependent context, just add the decl to the - // class's decl list and don't both with the lookup tables. This - // doesn't affect lookup because any call that might find this - // function via ADL necessarily has to involve dependently-typed - // arguments and hence can't be resolved until - // template-instantiation anyway. - if (CurContext->isDependentContext()) - CurContext->addHiddenDecl(FD); - else - CurContext->addDecl(FD); + // We only add the function declaration to the lookup tables, not + // the decl list, and only if the context isn't dependent. + if (!CurContext->isDependentContext()) + DC->makeDeclVisibleInContext(FD); + + FriendDecl *FrD = FriendDecl::Create(Context, CurContext, + D.getIdentifierLoc(), FD, + DS.getFriendSpecLoc()); + CurContext->addDecl(FrD); return DeclPtrTy::make(FD); } |